Package org.olat.commons.modules.bc.meta

Source Code of org.olat.commons.modules.bc.meta.MetaInfoFileImpl

/**
* 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) 1999-2006 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.commons.modules.bc.meta;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Date;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.dom4j.Document;
import org.dom4j.Element;
import org.olat.basesecurity.ManagerFactory;
import org.olat.core.commons.modules.bc.FolderConfig;
import org.olat.core.commons.modules.bc.meta.MetaInfo;
import org.olat.core.gui.util.CSSHelper;
import org.olat.core.id.Identity;
import org.olat.core.id.User;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.filter.FilterFactory;
import org.olat.core.util.vfs.OlatRelPathImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.xml.XMLParser;
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 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;
 
  // make it a factory
  public MetaInfoFileImpl() {
    super();
  }

  public MetaInfoFileImpl(File metaFile) {
    super();
    this.metaFile = metaFile;
  }
   
  /**
   * Lazy initialization: A meta file is created based on file data
   * if no meta file can be found for the given path.
   *
   * IMPORTANT: MetaInfa.getInstance can only create an instance, if the
   * referred bcPath exists. It needs this bcPath to determine wether it
   * is looking for a file or a folder meta info object.
   *
   * @param bcPath
   * @return MetaInfo instance or null if no instance could be created.
   */
  public MetaInfo createMetaInfoFor(OlatRelPathImpl olatRelPathImpl) {
    MetaInfoFileImpl meta = new MetaInfoFileImpl();
    if (!meta.init(olatRelPathImpl)) return null;
    return meta;
  }

  private boolean init(OlatRelPathImpl olatRelPathImpl) {
    String canonicalMetaPath = getCanonicalMetaPath(olatRelPathImpl);
    if (canonicalMetaPath == null) return false;
    originFile = getOriginFile(olatRelPathImpl);
    metaFile = new File(canonicalMetaPath);
    // set
    if (!parseSAX(metaFile)) {
      String metaDirPath = canonicalMetaPath.substring(0, canonicalMetaPath.lastIndexOf('/'));
      new File(metaDirPath).mkdirs();
      write();
    }
    return true;
  }

  private File getOriginFile(OlatRelPathImpl olatRelPathImpl) {
    return new File(FolderConfig.getCanonicalRoot() + olatRelPathImpl.getRelPath());
  }
  /**
   * Get the canonical path to the file's meta file.
   *
   * @param bcPath
   * @return String
   */
  private String getCanonicalMetaPath(OlatRelPathImpl olatRelPathImpl) {
    File f = getOriginFile(olatRelPathImpl);
    if (!f.exists()) return null;
    if (f.isDirectory()) {
      return FolderConfig.getCanonicalMetaRoot() + olatRelPathImpl.getRelPath() + "/.xml";
    } else {
      return FolderConfig.getCanonicalMetaRoot() + olatRelPathImpl.getRelPath() + ".xml";
    }
  }
 
  /**
   * 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(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 FileUtils.copyFileToDir(fSource, fTarget);
  }
 
  /**
   * 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
      metaFile.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 = ManagerFactory.getManager().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() {
    if (metaFile == null) return false;
    try {
      FileOutputStream fos = new FileOutputStream(metaFile);
      OutputStreamWriter sw = new OutputStreamWriter(fos, Charset.forName("UTF-8"));
      sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      sw.write("<meta>");
      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("</meta>");
      sw.close();
    } catch (Exception e) { return false; }
    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;
    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;
   
    try {
    //the performance gain of the SAX Parser over the DOM Parser allow
    //this to be synchronized (factory 5 to 10 quicker)
      synchronized(saxParser) {
        saxParser.parse(fMeta, this);
      }
    } 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);
    }
    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) {
        e.printStackTrace();
        //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 = ManagerFactory.getManager().loadIdentityByKey(authorIdentKey);
        if (identity == null) {
          log.warn("Found no idenitiy with key='" + authorIdentKey + "'");
          return "-";
        }
        return identity.getName();
      } catch (Exception e) {
        return "-";
      }
    } 
  }
 
  /**
   * @see org.olat.core.commons.modules.bc.meta.MetaInfo#getAuthorIdentity()
   */
  public Identity getAuthorIdentity() {
    if (this.authorIdentKey == null) {
      return null;
    } else {
      return ManagerFactory.getManager().loadIdentityByKey(this.authorIdentKey);
    }
  }

  /**
   * @see org.olat.core.commons.modules.bc.meta.MetaInfo#getHTMLFormattedAuthor()
   */
  public String getHTMLFormattedAuthor() {
    if (authorIdentKey == null) {
      return "-";
    } else {
      Identity identity = ManagerFactory.getManager().loadIdentityByKey(authorIdentKey);
      if (identity == null) {
        log.warn("Found no idenitiy with key='" + authorIdentKey + "'");
        return "-";
      }
      User user = identity.getUser();
      String formattedName = user.getProperty(UserConstants.FIRSTNAME, null);
      formattedName = formattedName + " " + user.getProperty(UserConstants.LASTNAME, null);
      //TODO: add link to user profile when checking in 4289/4295 and remove reference to loginname
      formattedName = formattedName + " (" + identity.getName() + ")";
      return formattedName;
   
  }
 

 
  /**
   * @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
   */
  public long getLastModified() { return originFile.lastModified(); }

  /**
   * @return size of file
   */
  public long getSize() {  return originFile.length(); }
 
  /**
   * @return formatted representation of size of file
   */
  public String getFormattedSize() { return StringHelper.formatMemory(getSize()); }

  /* ------------------------- Setters ------------------------------ */
 
  /**
   * @param string
   */
  public void setAuthor(String username) {
    Identity identity = ManagerFactory.getManager().findIdentityByName(username);
    if (identity == null) {
      log.warn("Found no idenitiy with username='" + username + "'");
      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;
  }

  /**
   * @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 ("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));
      }
    }
  }
 
  @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();
    }
    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;
  }
 
}
TOP

Related Classes of org.olat.commons.modules.bc.meta.MetaInfoFileImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.