Package org.olat.core.commons.modules.glossary

Source Code of org.olat.core.commons.modules.glossary.GlossaryItemManager

/**
* 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-2008 at frentix GmbH, Switzerland, http://www.frentix.com
* <p>
*/
package org.olat.core.commons.modules.glossary;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.olat.core.gui.control.generic.textmarker.TextMarker;
import org.olat.core.gui.control.generic.textmarker.TextMarkerManager;
import org.olat.core.gui.control.generic.textmarker.TextMarkerManagerImpl;
import org.olat.core.helpers.Settings;
import org.olat.core.id.OLATResourceable;
import org.olat.core.manager.BasicManager;
import org.olat.core.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.cache.n.CacheWrapper;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.coordinate.SyncerExecutor;
import org.olat.core.util.resource.OresHelper;
import org.olat.core.util.vfs.LocalFileImpl;
import org.olat.core.util.vfs.LocalFolderImpl;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.xml.XStreamHelper;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;

/**
* Description:<br>
* holds the glossary item list in a cache, to deliver it faster and to prevent
* from filesystem access or multiple loading into ram. each glossary will only
* be loaded once, until it changes and cache is invalidated. read and writes
* the enhanced docBook-glossary format from/to filesystem.
*
* @author Roman Haag, frentix GmbH, roman.haag@frentix.com
*/
public class GlossaryItemManager extends BasicManager {

  private static GlossaryItemManager INSTANCE = new GlossaryItemManager();

  private static final String OLD_GLOSSARY_FILENAME = "glossary.textmarker.xml";
  private static final String GLOSSARY_FILENAME = "glossary.xml";
  private static final String XML_GLOSSARY_ITEM_NAME = "glossentry";
 
  private static final String GLOSSARY_CONFIG_PROPERTIES_FILE = "glossary.properties";
  public static final String NO_MS_VALUE = "ms-none";
  public static final String MS_KEY = "morphological.service.identifier";
  public static final String REGISTER_ONOFF = "register.index.enabled";
 
  private static final OLATResourceable glossaryEventBus = OresHelper.createOLATResourceableType("glossaryEventBus");

  CacheWrapper glossaryCache;

  public GlossaryItemManager() {
    //just an object
  }

  public static GlossaryItemManager getInstance() {
    return INSTANCE;
  }

  /**
   * used to save new or changed entries in List
   *
   * @param olatResource
   * @param glossItemList
   */
  protected void saveGlossaryItemList(VFSContainer glossaryFolder, ArrayList<GlossaryItem> glossItemList) {
    VFSLeaf glossaryFile = getGlossaryFile(glossaryFolder);
    saveToFile(glossaryFile, glossItemList);
    glossItemList = removeEmptyGlossaryItems(glossItemList);
    updateCacheForGlossary(glossaryFolder, glossItemList);
  }
 
  /**
   * returns GlossaryItem-array containing only Items with a non-empty term
   * @param glossItemList
   * @return
   */
  private ArrayList<GlossaryItem> removeEmptyGlossaryItems(ArrayList<GlossaryItem> glossItemList){
    ArrayList<GlossaryItem> newList = new ArrayList<GlossaryItem>();
    for (Iterator<GlossaryItem> iterator = glossItemList.iterator(); iterator.hasNext();) {
      GlossaryItem glossaryItem = iterator.next();
      if (StringHelper.containsNonWhitespace(glossaryItem.getGlossTerm())){
        newList.add(glossaryItem);
      }
    }
    return newList;
  }

  /**
   * upgrades the old textmarker-format into the new DocBook-glossary-format
   *
   * @param folderContainingGlossary
   * @param textMarkerFile
   */
  protected void upgradeAndDeleteOldGlossary(VFSContainer folderContainingGlossary, VFSLeaf textMarkerFile) {
    // check if a new glossary exists, warn
    if (folderContainingGlossary.resolve(GLOSSARY_FILENAME) != null) {
      logError("Upgrading Glossary in " + folderContainingGlossary.toString() + ": There is already a new glossary-file. There can't be an old and a new version in the same directory!", null);
    } else { // upgrade it
      TextMarkerManager textMarkerManager = TextMarkerManagerImpl.getInstance();
      List<TextMarker> textMarkerList = textMarkerManager.loadTextMarkerList(textMarkerFile);
      Collections.sort(textMarkerList);
      ArrayList<GlossaryItem> glossaryItemArr = new ArrayList<GlossaryItem>();

      for (TextMarker tm : textMarkerList) {
        String glossTerm = tm.getMarkedMainText();
        String glossDef = tm.getHooverText();
        GlossaryItem glossItem = new GlossaryItem(glossTerm, glossDef);

        // handle alias -> save as synonyms
        String aliasString = tm.getMarkedAliasText();
        if (StringHelper.containsNonWhitespace(aliasString)) {
          String[] aliasArr = aliasString.split(";");
          ArrayList<String> glossSynonyms = new ArrayList<String>();
          glossSynonyms.addAll(Arrays.asList(aliasArr));
          glossItem.setGlossSynonyms(glossSynonyms);
        }
        glossaryItemArr.add(glossItem);
      }

      VFSLeaf glossaryFile = folderContainingGlossary.createChildLeaf(GLOSSARY_FILENAME);
      saveToFile(glossaryFile, glossaryItemArr);
      // keep a backup in debug mode:
      if (Settings.isDebuging()) {
        File tmFile = ((LocalFileImpl) textMarkerFile).getBasefile();
        File tmCont = ((LocalFolderImpl) folderContainingGlossary).getBasefile();
        FileUtils.copyFileToDir(tmFile, new File(tmCont + "/bkp"));
      }
      textMarkerFile.delete();
    }
  }


  /**
   * returns the glossary file. if an old-format is found in directory of the
   * glossary it automatically gets updated.
   *
   * @param folderContainingGlossary physical disk-path
   * @return the glossary file
   */
  public VFSLeaf getGlossaryFile(VFSContainer folderContainingGlossary) {
    VFSLeaf glossaryFile = (VFSLeaf) folderContainingGlossary.resolve(OLD_GLOSSARY_FILENAME);
    if (glossaryFile != null) {
      // old glossary
      upgradeAndDeleteOldGlossary(folderContainingGlossary, glossaryFile);
    }
    // look for new glossary, or use new after upgrading
    glossaryFile = (VFSLeaf) folderContainingGlossary.resolve(GLOSSARY_FILENAME);

    if (glossaryFile == null) {
      // create an empty file on the fly and initialize it
      glossaryFile = folderContainingGlossary.createChildLeaf(GLOSSARY_FILENAME);
      saveToFile(glossaryFile, new ArrayList<GlossaryItem>());
    }
    return glossaryFile;
  }
 
  public Long getGlossaryLastModifiedTime(VFSContainer folderContainingGlossary) {
    return getGlossaryFile(folderContainingGlossary).getLastModified();
  }

  public boolean isFolderContainingGlossary(VFSContainer folderContainingGlossary) {
    VFSLeaf glossaryFileOld = (VFSLeaf) folderContainingGlossary.resolve(OLD_GLOSSARY_FILENAME);
    VFSLeaf glossaryFileNew = (VFSLeaf) folderContainingGlossary.resolve(GLOSSARY_FILENAME);
    if (glossaryFileNew == null && glossaryFileOld == null) return false;
    else return true;
  }

  //TODO:RH:gloss improvement: dtd in xml files
  /**
   * writes glossary to xml-file
   * prepend doc-book dtd:
   * <!DOCTYPE glossary PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"          "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
   *
   * @param glossaryFile
   * @param glossaryItemArr
   */
  private void saveToFile(VFSLeaf glossaryFile, ArrayList<GlossaryItem> glossaryItemArr) {
    // cdata-tags should be used instead of strings, overwrite writer.
    XStream xstream = new XStream(new XppDriver() {
      public HierarchicalStreamWriter createWriter(Writer out) {
        return new PrettyPrintWriter(out) {
          protected void writeText(QuickWriter writer, String text) {
            if (text.contains("<")||text.contains(">")||text.contains("&")){
              writer.write("<![CDATA[");
              writer.write(text);
              writer.write("]]>");
            } else {
              writer.write(text);
            }
          }
        };
      }
    });

    xstream.alias(XML_GLOSSARY_ITEM_NAME, GlossaryItem.class);
    glossaryItemArr = removeEmptyGlossaryItems(glossaryItemArr);
    XStreamHelper.writeObject(xstream, glossaryFile, glossaryItemArr);
  }

 
  //FIXME: VFSItem should be capable of returning an identifier, instead of casting to LocalFolderImpl implement a getIdentifier for it!
  @SuppressWarnings("unchecked")
  public ArrayList<GlossaryItem> getGlossaryItemListByVFSItem(final VFSContainer glossaryFolder){   
    final String glossaryKey = ((LocalFolderImpl)glossaryFolder).getBasefile().toString();
    if (glossaryCache == null) {
      CoordinatorManager.getCoordinator().getSyncer().doInSync(glossaryEventBus, new SyncerExecutor() {
        public void  execute() {
          if (glossaryCache == null) {
            glossaryCache = CoordinatorManager.getCoordinator().getCacher().getOrCreateCache(this.getClass(), "glossary");
          }
        }
      });
    }
    //try to load from cache
    ArrayList<GlossaryItem> glossaryItemList = (ArrayList<GlossaryItem>) glossaryCache.get(glossaryKey);
    if (glossaryItemList != null){
      if (isLogDebugEnabled()){
        logDebug("Loading glossary from cache.", null);
      }
      return glossaryItemList;
    }
    // load from filesystem
    CoordinatorManager.getCoordinator().getSyncer().doInSync(glossaryEventBus, new SyncerExecutor() {
      @SuppressWarnings("synthetic-access")
      public void execute() {
        ArrayList<GlossaryItem> glossaryItemListTemp = new ArrayList<GlossaryItem>();
        if (isLogDebugEnabled()){
          logDebug("Loading glossary from filesystem. Glossary folder: " + glossaryFolder, null);
        }
        glossaryItemListTemp = loadGlossaryItemListFromFile(getGlossaryFile(glossaryFolder));
        glossaryCache.put(glossaryKey, glossaryItemListTemp);       
      }
    });
    //return value from cache, as it was put in there before
    return (ArrayList<GlossaryItem>) glossaryCache.get(glossaryKey);
  }

  /**
   * if changes occur on GlossaryList, cache has to be updated
   *
   * @param olatResource
   */
  //FIXME: VFSItem should be capable of returning an identifier, instead of casting to LocalFolderImpl implement a getIdentifier for it!
  private void updateCacheForGlossary(VFSContainer glossaryFolder, ArrayList<GlossaryItem> glossItemList) {
    final String glossaryKey = ((LocalFolderImpl)glossaryFolder).getBasefile().toString();
    glossaryCache.update(glossaryKey, glossItemList);
  }

  /**
   * load a list of glossaryItem from glossary.xml - File
   *
   * @param glossaryFile
   * @return list with GlossaryItem's
   */
  @SuppressWarnings("unchecked")
  private ArrayList<GlossaryItem> loadGlossaryItemListFromFile(VFSLeaf glossaryFile) {
    ArrayList<GlossaryItem> glossaryItemList = new ArrayList<GlossaryItem>();
    if (glossaryFile == null) { return new ArrayList<GlossaryItem>(); }
    XStream xstream = XStreamHelper.createXStreamInstance();
    xstream.alias(XML_GLOSSARY_ITEM_NAME, GlossaryItem.class);
    Object glossObj = XStreamHelper.readObject(xstream, glossaryFile.getInputStream());
    if (glossObj instanceof ArrayList) {
      ArrayList<GlossaryItem> glossItemsFromFile = (ArrayList<GlossaryItem>) glossObj;
      glossaryItemList.addAll(glossItemsFromFile);
    } else {
      logError("The Glossary-XML-File " + glossaryFile.toString() + " seems not to be correct!", null);
    }

    Collections.sort(glossaryItemList);
    return glossaryItemList;
  }

  /**
   * needed for search engine
   * @param olatResource
   * @return
   */
  public String getGlossaryContent(VFSContainer glossaryFolder){
    ArrayList<GlossaryItem> glossItems = getGlossaryItemListByVFSItem(glossaryFolder);
    StringBuilder sb = new StringBuilder();
    for (GlossaryItem glossItem : glossItems) {
      ArrayList<String> allStrings = glossItem.getAllStringsToMarkup();
      for (String markupStr : allStrings) {
        sb.append(markupStr);
        sb.append("\n");
      }
      sb.append("\n");
      sb.append(glossItem.getGlossDef());
      sb.append("\n\n");
    }
    return sb.toString();
  }
 
 
 
//      Configuration of a glossary in properties file
  //TODO: RH: improvement in case fileReads should slow down system
  // implement a GlossaryObject with settings and glossItemList and cache this.
  public Properties getGlossaryConfig(VFSContainer glossaryFolder){
    Properties props = new Properties();
    VFSLeaf glossProp = (VFSLeaf) glossaryFolder.resolve(GLOSSARY_CONFIG_PROPERTIES_FILE);
    if(glossProp!=null){
      try {
        props.load(glossProp.getInputStream());
      } catch (IOException e) {
        logError("Properties in " + glossProp + " could not be read.", e);
      }
    } else {
      //set default config
      props.put(MS_KEY, NO_MS_VALUE);
      props.put(REGISTER_ONOFF, "true");
      setGlossaryConfig(glossaryFolder, props);
    }
   
   
    return props;
  }
 
 
  public void setGlossaryConfig(VFSContainer glossaryFolder, Properties props){
    VFSLeaf glossProp = (VFSLeaf) glossaryFolder.resolve(GLOSSARY_CONFIG_PROPERTIES_FILE);
    if (glossProp==null){
      glossProp = glossaryFolder.createChildLeaf(GLOSSARY_CONFIG_PROPERTIES_FILE);
    }
    try {
      props.store(glossProp.getOutputStream(false), "Settings for the glossary saved in this folder.");
    } catch (IOException e) {
      logError("Properties in " + glossProp + " could not be written.", e);
    }
   
  }
 
}
TOP

Related Classes of org.olat.core.commons.modules.glossary.GlossaryItemManager

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.