/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.commons.modules.bc.meta;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.poi.util.IOUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.olat.basesecurity.BaseSecurityManager;
import org.olat.core.commons.services.thumbnail.CannotGenerateThumbnailException;
import org.olat.core.commons.services.thumbnail.FinalSize;
import org.olat.core.commons.services.thumbnail.ThumbnailService;
import org.olat.core.gui.util.CSSHelper;
import org.olat.core.id.Identity;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.Formatter;
import org.olat.core.util.StringHelper;
import org.olat.core.util.filter.FilterFactory;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.OlatRelPathImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.xml.XMLParser;
import org.olat.user.UserManager;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Initial Date: 08.07.2003
*
* @author Mike Stock<br>
*
* Comment:
* Meta files are in a shadow filesystem with the same directory structure as
* their original files. Meta info for directories is stored in a file called
* ".xml" residing in the respective directory. Meta info for files is stored
* in a file with ".xml" appended to its filename.
*
*/
public class MetaInfoFileImpl extends DefaultHandler implements MetaInfo {
private static OLog log = Tracing.createLoggerFor(MetaInfoFileImpl.class);
private static SAXParser saxParser;
static {
try {
saxParser = SAXParserFactory.newInstance().newSAXParser();
} catch(Exception ex) {
log.error("", ex);
}
}
// meta data
private String uuid;
private Long authorIdentKey = null;
private Long lockedByIdentKey = null;
private String comment = "";
private String title, publisher, creator, source, city, pages, language, url, pubMonth, pubYear;
private Date lockedDate;
private int downloadCount;
private boolean locked;
// internal
private File originFile = null;
private File metaFile = null;
private boolean cannotGenerateThumbnail = false;
private List<Thumbnail> thumbnails = new ArrayList<Thumbnail>();
private ThumbnailService thumbnailService;
// make it a factory
private MetaInfoFileImpl() {
//
}
public MetaInfoFileImpl(File metaFile) {
this.metaFile = metaFile;
parseSAX(metaFile);
}
protected MetaInfoFileImpl(String canonicalMetaPath, File metaFile, File originFile) {
this.metaFile = metaFile;
this.originFile = originFile;
// set
if (!parseSAX(metaFile)) {
String metaDirPath = canonicalMetaPath.substring(0, canonicalMetaPath.lastIndexOf('/'));
new File(metaDirPath).mkdirs();
if(uuid == null) {
generateUUID();
}
write();
}
}
public void setThumbnailService(ThumbnailService thumbnailService) {
this.thumbnailService = thumbnailService;
}
/**
* Rename the given meta info file
*
* @param meta
* @param newName
*/
public void rename(String newName) {
// rename meta info file name
if (isDirectory()) { // rename the directory, which is the parent of the actual ".xml" file
File metaFileDirectory = metaFile.getParentFile();
metaFileDirectory.renameTo(new File(metaFileDirectory.getParentFile(), newName));
} else { // rename the file
metaFile.renameTo(new File(metaFile.getParentFile(), newName + ".xml"));
}
}
/**
* Move/Copy the given meta info to the target directory.
* @param targetDir
* @param move
*/
public void moveCopyToDir(OlatRelPathImpl target, boolean move) {
File fSource = metaFile;
File fTarget = new File(MetaInfoFactory.getCanonicalMetaPath(target));
if (isDirectory()) { // move/copy whole meta directory
fSource = fSource.getParentFile();
fTarget = fTarget.getParentFile();
} else if (target instanceof VFSContainer) {
//getCanonicalMetaPath give the path to the xml file where the metadatas are saved
if(fTarget.getName().equals(".xml")) {
fTarget = fTarget.getParentFile();
}
}
if (move) FileUtils.moveFileToDir(fSource, fTarget);
else {
//copy
Map<String,String> pathToUuid = new HashMap<String,String>();
File mTarget = new File(fTarget, fSource.getName());
collectUUIDRec(mTarget, pathToUuid);
if(FileUtils.copyFileToDir(fSource, fTarget, "copy metadata")) {
File endTarget = new File(fTarget, fSource.getName());
generateUUIDRec(endTarget, pathToUuid);
}
}
}
private void collectUUIDRec(File mTarget, Map<String,String> pathToUuid) {
try {
if(mTarget.exists()) {
if(mTarget.isDirectory()) {
//TODO
} else {
MetaInfoFileImpl copyMeta = new MetaInfoFileImpl();
copyMeta.metaFile = mTarget;
if (copyMeta.parseSAX(mTarget)) {
pathToUuid.put(mTarget.getCanonicalPath(), copyMeta.getUUID());
}
}
}
} catch (IOException e) {
log.error("cannot collect current UUID before copy", e);
}
}
private void generateUUIDRec(File endTarget, Map<String,String> pathToUuid) {
if(!endTarget.exists()) {
return;
}
try {
if(endTarget.isDirectory()) {
for(File subEndTarget:endTarget.listFiles(new XmlFilter())) {
generateUUIDRec(subEndTarget, pathToUuid);
}
} else {
MetaInfoFileImpl copyMeta = new MetaInfoFileImpl();
copyMeta.metaFile = endTarget;
if (copyMeta.parseSAX(endTarget)) {
String tempUuid = pathToUuid.get(endTarget.getCanonicalPath());
if(StringHelper.containsNonWhitespace(tempUuid)) {
copyMeta.uuid =tempUuid;
} else {
copyMeta.generateUUID();
}
copyMeta.write();
}
}
} catch (IOException e) {
log.error("Cannot generate a new uuid on copy", e);
}
}
/**
* Delete all associated meta info including sub files/directories
* @param meta
*/
public void deleteAll() {
if (isDirectory()) { // delete whole meta directory (where the ".xml" resides within)
FileUtils.deleteDirsAndFiles(metaFile.getParentFile(), true, true);
} else { // delete this single meta file
delete();
}
}
/**
* Copy values from froMeta into this object except name.
* @param fromMeta
*/
public void copyValues(MetaInfo fromMeta) {
this.setAuthor(fromMeta.getAuthor());
this.setComment(fromMeta.getComment());
this.setCity(fromMeta.getCity());
this.setCreator(fromMeta.getCreator());
this.setLanguage(fromMeta.getLanguage());
this.setPages(fromMeta.getPages());
this.setPublicationDate(fromMeta.getPublicationDate()[1], fromMeta.getPublicationDate()[0]);
this.setPublisher(fromMeta.getPublisher());
this.setSource(fromMeta.getSource());
this.setTitle(fromMeta.getTitle());
this.setUrl(fromMeta.getUrl());
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
if(!locked) {
lockedByIdentKey = null;
lockedDate = null;
}
}
public Identity getLockedByIdentity() {
if(lockedByIdentKey != null) {
Identity identity = BaseSecurityManager.getInstance().loadIdentityByKey(lockedByIdentKey);
return identity;
}
return null;
}
public Long getLockedBy() {
return lockedByIdentKey;
}
public void setLockedBy(Long lockedBy) {
this.lockedByIdentKey = lockedBy;
}
public Date getLockedDate() {
return lockedDate;
}
public void setLockedDate(Date lockedDate) {
this.lockedDate = lockedDate;
}
/**
* Writes the meta data to file. If no changes have been made,
* does not write anything.
* @return True upon success.
*/
public boolean write() {
BufferedOutputStream bos = null;
if (metaFile == null) return false;
try {
bos = new BufferedOutputStream(new FileOutputStream(metaFile));
OutputStreamWriter sw = new OutputStreamWriter(bos, Charset.forName("UTF-8"));
sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sw.write("<meta");
if(StringHelper.containsNonWhitespace(uuid)) {
sw.write(" uuid=\"" + uuid + "\"");
}
sw.write(">");
sw.write("<author><![CDATA[" + (authorIdentKey == null ? "" : authorIdentKey.toString()) + "]]></author>");
sw.write("<lock locked=\"" + locked + "\"" + (lockedDate == null ? "" : " date=\"" + lockedDate.getTime() + "\"") + "><![CDATA[" + (lockedByIdentKey == null ? "" : lockedByIdentKey) + "]]></lock>");
sw.write("<comment><![CDATA[" + filterForCData(comment) + "]]></comment>");
sw.write("<title><![CDATA[" + filterForCData(title) + "]]></title>");
sw.write("<publisher><![CDATA[" + filterForCData(publisher) + "]]></publisher>");
sw.write("<creator><![CDATA[" + filterForCData(creator) + "]]></creator>");
sw.write("<source><![CDATA[" + filterForCData(source) + "]]></source>");
sw.write("<city><![CDATA[" + filterForCData(city) + "]]></city>");
sw.write("<pages><![CDATA[" + filterForCData(pages) + "]]></pages>");
sw.write("<language><![CDATA[" + filterForCData(language) + "]]></language>");
sw.write("<url><![CDATA[" + filterForCData(url) + "]]></url>");
sw.write("<publicationDate><month><![CDATA[" + (pubMonth != null ? pubMonth.trim() : "") + "]]></month><year><![CDATA[" + (pubYear != null ? pubYear.trim() : "") + "]]></year></publicationDate>");
sw.write("<downloadCount><![CDATA[" + downloadCount + "]]></downloadCount>");
sw.write("<thumbnails cannotGenerateThumbnail=\"" + cannotGenerateThumbnail + "\">");
for(Thumbnail thumbnail:thumbnails) {
sw.write("<thumbnail maxHeight=\"");
sw.write(Integer.toString(thumbnail.getMaxHeight()));
sw.write("\" maxWidth=\"");
sw.write(Integer.toString(thumbnail.getMaxWidth()));
sw.write("\" finalHeight=\"");
sw.write(Integer.toString(thumbnail.getFinalHeight()));
sw.write("\" finalWidth=\"");
sw.write(Integer.toString(thumbnail.getFinalWidth()));
sw.write("\">");
sw.write("<![CDATA[" + thumbnail.getThumbnailFile().getName() + "]]>");
sw.write("</thumbnail>");
}
sw.write("</thumbnails>");
sw.write("</meta>");
sw.close();
} catch (Exception e) {
return false;
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
log.warn("Can not close stream, "+ e.getMessage());
}
}
}
return true;
}
private String filterForCData(String original) {
if(StringHelper.containsNonWhitespace(original)) {
return FilterFactory.getXMLValidCharacterFilter().filter(original);
}
return "";
}
/**
* Delete this meta info
*
* @return True upon success.
*/
public boolean delete() {
if (metaFile == null) return false;
for(Thumbnail thumbnail:thumbnails) {
File file = thumbnail.getThumbnailFile();
if(file != null && file.exists()) {
file.delete();
}
}
return metaFile.delete();
}
/**
* The parser is synchronized. Normally for such small files, this is
* the quicker way. Creation of a SAXParser is really time consuming.
* An other possibilty would be to use a pool of parser.
* @param fMeta
* @return
*/
private boolean parseSAX(File fMeta) {
if (fMeta == null || !fMeta.exists() || fMeta.isDirectory()) return false;
InputStream in = null;
try {
//the performance gain of the SAX Parser over the DOM Parser allow
//this to be synchronized (factory 5 to 10 quicker)
synchronized(saxParser) {
in = new FileInputStream(fMeta);
saxParser.parse(in, this);
if(uuid == null) {
generateUUID();
write();
}
}
} catch (SAXParseException ex) {
if(!parseSAXFiltered(fMeta)) {
//OLAT-5383,OLAT-5468: lowered error to warn to reduce error noise
log.warn("SAX Parser error while parsing " + fMeta, ex);
}
} catch(Exception ex) {
log.error("Error while parsing " + fMeta, ex);
} finally {
IOUtils.closeQuietly(in);
}
return true;
}
/**
* Try to rescue xml files with invalid characters
* @param fMeta
* @return true if rescue is successful
*/
private boolean parseSAXFiltered(File fMeta) {
String original = FileUtils.load(fMeta, "UTF-8");
if(original == null) return false;
String filtered = FilterFactory.getXMLValidCharacterFilter().filter(original);
if(original != null && !original.equals(filtered)) {
try {
synchronized(saxParser) {
InputSource in = new InputSource(new StringReader(filtered));
saxParser.parse(in, this);
}
write();//update with the new filtered write method
return true;
} catch (Exception e) {
//only a fallback, fail silently
}
}
return false;
}
/**
* Parse XML from file with SAX and fill-in MetaInfo attributes.
* @param fMeta
*/
@Deprecated
public boolean parseXMLdom(File fMeta) {
if (fMeta == null || !fMeta.exists()) return false;
InputStream is;
try {
is = new BufferedInputStream(new FileInputStream(fMeta));
} catch (FileNotFoundException e) {
return false;
}
try {
XMLParser xmlp = new XMLParser();
Document doc = xmlp.parse(is, false);
if (doc == null) return false;
// extract data from XML
Element root = doc.getRootElement();
Element n;
n = root.element("author");
if (n == null) {
authorIdentKey = null;
} else {
if (n.getText().length() == 0 ) {
authorIdentKey = null;
} else {
try {
authorIdentKey = Long.valueOf(n.getText());
} catch (NumberFormatException nEx) {
authorIdentKey = null;
}
}
}
n = root.element("comment");
comment = (n != null) ? n.getText() : "";
Element lockEl = root.element("lock");
if(lockEl != null) {
locked = "true".equals(lockEl.attribute("locked").getValue());
try {
lockedByIdentKey = new Long(n.getText());
} catch (NumberFormatException nEx) {
lockedByIdentKey = null;
}
}
n = root.element("title");
title = (n != null) ? n.getText() : "";
n = root.element("publisher");
publisher = (n != null) ? n.getText() : "";
n = root.element("source");
source = (n != null) ? n.getText() : "";
n = root.element("creator");
creator = (n != null) ? n.getText() : "";
n = root.element("city");
city = (n != null) ? n.getText() : "";
n = root.element("pages");
pages = (n != null) ? n.getText() : "";
n = root.element("language");
language = (n != null) ? n.getText() : "";
n = root.element("url");
url = (n != null) ? n.getText() : "";
n = root.element("downloadCount");
downloadCount = (n != null) ? Integer.valueOf(n.getText()) : 0;
n = root.element("publicationDate");
if (n != null) {
Element m = n.element("month");
pubMonth = (m != null) ? m.getText() : "";
m = n.element("year");
pubYear = (m != null) ? m.getText() : "";
}
return true;
} catch (Exception ex) {
log.warn("Corrupted metadata file: " + fMeta);
return false;
}
}
/* ------------------------- Getters ------------------------------ */
/**
* @return name of the initial author
*/
public String getAuthor() {
if (authorIdentKey == null) {
return "-";
} else {
try {
Identity identity = BaseSecurityManager.getInstance().loadIdentityByKey(authorIdentKey);
if (identity == null) {
log.warn("Found no idenitiy with key='" + authorIdentKey + "'");
return "-";
}
return identity.getName();
} catch (Exception e) {
return "-";
}
}
}
@Override
public String getUUID() {
return uuid;
}
public void generateUUID() {
uuid = UUID.randomUUID().toString().replace("-", "");
}
@Override
public Long getAuthorIdentityKey() {
return authorIdentKey;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getAuthorIdentity()
*/
@Override
public Identity getAuthorIdentity() {
if (authorIdentKey == null) {
return null;
} else {
return BaseSecurityManager.getInstance().loadIdentityByKey(authorIdentKey);
}
}
@Override
public boolean hasAuthorIdentity() {
return (authorIdentKey != null);
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getHTMLFormattedAuthor()
*/
public String getHTMLFormattedAuthor() {
if (authorIdentKey == null) {
return "-";
} else {
String fullName = UserManager.getInstance().getUserDisplayName(authorIdentKey);
if (fullName == null) {
log.warn("Found no identity with key='" + authorIdentKey + "'");
return "-";
}
return fullName;
}
}
/**
* @return comment
*/
public String getComment() { return comment; }
public String getName() { return originFile.getName(); }
/**
* @return True if this is a directory
*/
public boolean isDirectory() { return originFile.isDirectory(); }
/**
* @return Last modified timestamp
*/
@Override
public long getLastModified() {
return originFile.lastModified();
}
/**
* @return The last modification date of the metadata
*/
@Override
public Date getMetaLastModified() {
if(metaFile == null) return null;
long lastModified = metaFile.lastModified();
return lastModified > 0 ? new Date(lastModified) : null;
}
/**
* @return size of file
*/
public long getSize() { return originFile.length(); }
/**
* @return formatted representation of size of file
*/
public String getFormattedSize() { return Formatter.formatBytes(getSize()); }
/* ------------------------- Setters ------------------------------ */
/**
* @param string
*/
public void setAuthor(String username) {
Identity identity = BaseSecurityManager.getInstance().findIdentityByName(username);
if (identity == null) {
log.warn("Found no idenitiy with username='" + username + "'");
authorIdentKey = null;
return;
}
authorIdentKey = identity.getKey();
}
@Override
public void setAuthor(Identity identity) {
if (identity == null) {
log.warn("Found no idenity");
authorIdentKey = null;
return;
}
authorIdentKey = identity.getKey();
}
/**
* @param string
*/
public void setComment(String string) { comment = string; }
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Name [" + getName());
sb.append("] Author [" + getAuthor());
sb.append("] Comment [" + getComment());
sb.append("] IsDirectory [" + isDirectory());
sb.append("] Size [" + getFormattedSize());
sb.append("] LastModified [" + new Date(getLastModified()) + "]");
return sb.toString();
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getCity()
*/
public String getCity() {
return city;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getLanguage()
*/
public String getLanguage() {
return language;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getPages()
*/
public String getPages() {
return pages;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getPublishDate()
*/
public String[] getPublicationDate() {
return new String[] { pubYear, pubMonth };
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getPublisher()
*/
public String getPublisher() {
return publisher;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getCreator()
*/
public String getCreator() {
return creator;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getSource()
*/
public String getSource() {
return source;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getTitle()
*/
public String getTitle() {
return title;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getUrl()
*/
public String getUrl() {
return url;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setCity(java.lang.String)
*/
public void setCity(String city) {
this.city = city;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setLanguage(java.lang.String)
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setPages(java.lang.String)
*/
public void setPages(String pages) {
this.pages = pages;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setPublishDate(java.lang.String)
*/
public void setPublicationDate(String month, String year) {
this.pubMonth = month;
this.pubYear = year;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setPublisher(java.lang.String)
*/
public void setPublisher(String publisher) {
this.publisher = publisher;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setWriter(java.lang.String)
*/
public void setWriter(String writer) {
this.creator = writer;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setWriter(java.lang.String)
*/
public void setCreator(String creator) {
this.creator = creator;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setSource(java.lang.String)
*/
public void setSource(String source) {
this.source = source;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setTitle(java.lang.String)
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#setUrl(java.lang.String)
*/
public void setUrl(String url) {
this.url = url;
}
public boolean isThumbnailAvailable() {
if(isDirectory()) return false;
if(originFile.isHidden()) return false;
if(cannotGenerateThumbnail) return false;
VFSLeaf originLeaf = new LocalFileImpl(originFile);
if (thumbnailService != null) return thumbnailService.isThumbnailPossible(originLeaf);
return false;
}
public VFSLeaf getThumbnail(int maxWidth, int maxHeight, boolean fill) {
if(isDirectory()) return null;
Thumbnail thumbnailInfo = getThumbnailInfo(maxWidth, maxHeight, fill);
if(thumbnailInfo == null) {
return null;
}
return new LocalFileImpl(thumbnailInfo.getThumbnailFile());
}
/**
* Thumbnails are cleared and the XML file is written on the disk
*/
@Override
public void clearThumbnails() {
cannotGenerateThumbnail = false;
for(Thumbnail thumbnail:thumbnails) {
File thumbnailFile = thumbnail.getThumbnailFile();
if(thumbnailFile != null && thumbnailFile.exists()) {
thumbnailFile.delete();
}
}
thumbnails.clear();
write();
}
private Thumbnail getThumbnailInfo(int maxWidth, int maxHeight, boolean fill) {
for(Thumbnail thumbnail:thumbnails) {
if(maxHeight == thumbnail.getMaxHeight() && maxWidth == thumbnail.getMaxWidth()) {
if(thumbnail.exists()) {
return thumbnail;
}
}
}
//generate a file name
File metaLoc = metaFile.getParentFile();
String name = originFile.getName();
String extension = FileUtils.getFileSuffix(name);
String nameOnly = name.substring(0, name.length() - extension.length() - 1);
String randuuid = UUID.randomUUID().toString();
String thumbnailExtension = preferedThumbnailType(extension);
File thumbnailFile = new File(metaLoc, nameOnly + "_" + randuuid + "_" + maxHeight + "x" + maxWidth + (fill ? "xfill" : "") + "." + thumbnailExtension);
//generate thumbnail
long start = 0l;
if(log.isDebug()) start = System.currentTimeMillis();
VFSLeaf thumbnailLeaf = new LocalFileImpl(thumbnailFile);
VFSLeaf originLeaf = new LocalFileImpl(originFile);
if(thumbnailService != null &&thumbnailService.isThumbnailPossible(thumbnailLeaf)) {
try {
if(thumbnails.isEmpty()) {
//be paranoid
cannotGenerateThumbnail = true;
write();
}
log.info("Start thumbnail: " + thumbnailLeaf);
FinalSize finalSize = thumbnailService.generateThumbnail(originLeaf, thumbnailLeaf, maxWidth, maxHeight, fill);
if(finalSize == null) {
return null;
} else {
Thumbnail thumbnail = new Thumbnail();
thumbnail.setMaxHeight(maxHeight);
thumbnail.setMaxWidth(maxWidth);
thumbnail.setFinalHeight(finalSize.getHeight());
thumbnail.setFinalWidth(finalSize.getWidth());
thumbnail.setFill(true);
thumbnail.setThumbnailFile(thumbnailFile);
thumbnails.add(thumbnail);
cannotGenerateThumbnail = false;
write();
log.info("Create thumbnail: " + thumbnailLeaf);
if(log.isDebug()) {
log.debug("Creation of thumbnail takes (ms): " + (System.currentTimeMillis() - start));
}
return thumbnail;
}
} catch (CannotGenerateThumbnailException e) {
//don't try every time to create the thumbnail.
cannotGenerateThumbnail = true;
write();
return null;
}
}
return null;
}
private String preferedThumbnailType(String extension) {
if(extension.equalsIgnoreCase("png") || extension.equalsIgnoreCase("gif")) {
return extension;
}
if(extension.equalsIgnoreCase("pdf")) {
return "png";
}
return "jpg";
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#increaseDownloadCount()
*/
public void increaseDownloadCount() {
this.downloadCount++;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getDownloadCount()
*/
public int getDownloadCount() {
return downloadCount;
}
public void setAuthorIdentKey(Long authorIdentKey) {
this.authorIdentKey = authorIdentKey;
}
private StringBuilder current;
////////////////////////////////////
// SAX Handler for max. performance
////////////////////////////////////
@Override
public final void startElement(String uri, String localName, String qName, Attributes attributes) {
if("meta".equals(qName)) {
uuid = attributes.getValue("uuid");
} else if ("lock".equals(qName)) {
locked ="true".equals(attributes.getValue("locked"));
String date = attributes.getValue("date");
if (date != null && date.length() > 0) {
lockedDate = new Date(Long.parseLong(date));
}
} else if ("thumbnails".equals(qName)) {
String valueStr = attributes.getValue("cannotGenerateThumbnail");
if(StringHelper.containsNonWhitespace(valueStr)) {
cannotGenerateThumbnail = new Boolean(valueStr);
}
} else if ("thumbnail".equals(qName)) {
Thumbnail thumbnail = new Thumbnail();
thumbnail.setMaxHeight(Integer.parseInt(attributes.getValue("maxHeight")));
thumbnail.setMaxWidth(Integer.parseInt(attributes.getValue("maxWidth")));
thumbnail.setFinalHeight(Integer.parseInt(attributes.getValue("finalHeight")));
thumbnail.setFinalWidth(Integer.parseInt(attributes.getValue("finalWidth")));
thumbnail.setFill("true".equals(attributes.getValue("fill")));
thumbnails.add(thumbnail);
}
}
@Override
public final void characters(char[] ch, int start, int length) {
if(length == 0) return;
if(current == null) {
current = new StringBuilder();
}
current.append(ch, start, length);
}
@Override
public final void endElement(String uri, String localName, String qName) {
if(current == null) return;
if("comment".equals(qName)) {
comment = current.toString();
} else if ("author".equals(qName)) {
try {
authorIdentKey = Long.valueOf(current.toString());
} catch (NumberFormatException nEx) {
//nothing to say
}
} else if ("lock".equals(qName)) {
try {
lockedByIdentKey = new Long(current.toString());
} catch (NumberFormatException nEx) {
//nothing to say
}
} else if ("title".equals(qName)) {
title = current.toString();
} else if ("publisher".equals(qName)) {
publisher = current.toString();
} else if ("source".equals(qName)) {
source = current.toString();
} else if ("city".equals(qName)) {
city = current.toString();
} else if ("pages".equals(qName)) {
pages = current.toString();
} else if ("language".equals(qName)) {
language = current.toString();
} else if ("downloadCount".equals(qName)) {
try {
downloadCount = Integer.valueOf(current.toString());
} catch (NumberFormatException nEx) {
//nothing to say
}
} else if ("month".equals(qName)) {
pubMonth = current.toString();
} else if ("year".equals(qName)) {
pubYear = current.toString();
} else if (qName.equals("creator")) {
this.creator = current.toString();
} else if (qName.equals("url")) {
this.url = current.toString();
} else if (qName.equals("thumbnail")) {
String finalName = current.toString();
File thumbnailFile = new File(metaFile.getParentFile(), finalName);
thumbnails.get(thumbnails.size() - 1).setThumbnailFile(thumbnailFile);
}
current = null;
}
/**
* @see org.olat.core.commons.modules.bc.meta.MetaInfo#getIconCssClass()
*/
@Override
public String getIconCssClass() {
String cssClass;
if (isDirectory()) {
cssClass = CSSHelper.CSS_CLASS_FILETYPE_FOLDER;
} else {
cssClass = CSSHelper.createFiletypeIconCssClassFor(getName());
}
return cssClass;
}
public static class XmlFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.getName().endsWith(".xml");
}
}
public static class Thumbnail {
private int maxWidth;
private int maxHeight;
private int finalWidth;
private int finalHeight;
private boolean fill = false;
private File thumbnailFile;
public int getMaxWidth() {
return maxWidth;
}
public void setMaxWidth(int maxWidth) {
this.maxWidth = maxWidth;
}
public int getMaxHeight() {
return maxHeight;
}
public void setMaxHeight(int maxHeight) {
this.maxHeight = maxHeight;
}
public int getFinalWidth() {
return finalWidth;
}
public void setFinalWidth(int finalWidth) {
this.finalWidth = finalWidth;
}
public int getFinalHeight() {
return finalHeight;
}
public void setFinalHeight(int finalHeight) {
this.finalHeight = finalHeight;
}
public boolean isFill() {
return fill;
}
public void setFill(boolean fill) {
this.fill = fill;
}
public File getThumbnailFile() {
return thumbnailFile;
}
public void setThumbnailFile(File thumbnailFile) {
this.thumbnailFile = thumbnailFile;
}
public boolean exists() {
return thumbnailFile == null ? false : thumbnailFile.exists();
}
}
}