/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Nomad is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.sf.nmedit.jtheme.store; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.nmedit.jtheme.JTContext; import net.sf.nmedit.jtheme.JTException; import net.sf.nmedit.jtheme.image.ImageCache; import net.sf.nmedit.jtheme.image.ImageResource; import net.sf.nmedit.jtheme.store2.ButtonElement; import net.sf.nmedit.jtheme.store2.ComponentElement; import net.sf.nmedit.jtheme.store2.ConnectorElement; import net.sf.nmedit.jtheme.store2.ImageElement; import net.sf.nmedit.jtheme.store2.LabelElement; import net.sf.nmedit.jtheme.store2.LightElement; import net.sf.nmedit.jtheme.store2.ModuleElement; import net.sf.nmedit.jtheme.store2.SliderElement; import net.sf.nmedit.jtheme.store2.TextDisplayElement; import net.sf.nmedit.nmutils.PluginObjectInputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.w3c.dom.css.CSSStyleSheet; import org.xml.sax.InputSource; import com.steadystate.css.parser.CSSOMParser; public class DefaultStorageContext extends StorageContext { private static final Log log = LogFactory.getLog(DefaultStorageContext.class); private static final boolean DEBUG = false; private Map<Object, ModuleElement> moduleStoreMap = new HashMap<Object, ModuleElement>(); private ClassLoader classLoader; private CSSStyleSheet styleSheet; private Map<String, ImageResource> imageResourceMap = new HashMap<String, ImageResource>(); private ImageCache imageCache = new ImageCache(); private static transient Log _logger; private static Log getLogger() { if (_logger == null) _logger = LogFactory.getLog(DefaultStorageContext.class); return _logger; } public ImageCache getImageCache() { return imageCache; } public DefaultStorageContext(JTContext context, ClassLoader classLoader) { this.classLoader = classLoader; this.context = context; installDefaults(); } private JTContext context; public JTContext getContext() { return context; } public ImageResource getCachedImage(URL source) { if (source != null) { return imageResourceMap.get(source.toString()); } return null; } public ImageResource getCachedImage(String source) { ImageResource ir = imageResourceMap.get(source); if (ir == null) { ClassLoader loader = classLoader != null ? classLoader :getClass().getClassLoader(); URL url = loader.getResource(source); ir = getCachedImage(url); } return ir; } /* public void putImage(URL url, ImageResource ir) { if (url != null) { if (ir.getImageCache() != null) ir.setImageCache(imageCache); imageResourceMap.put(url, ir); } }*/ public void putImage(String key, ImageResource ir) { if (key != null) { if (ir.getImageCache() != null) ir.setImageCache(imageCache); imageResourceMap.put(key, ir); } } public CSSStyleSheet getStyleSheet() { return styleSheet; } public ClassLoader getContextClassLoader() { return classLoader; } protected void installDefaults() { installStore("module", ModuleElement.class); installStore("label", LabelElement.class); installStore("image", ImageElement.class); installStore("connector", ConnectorElement.class); installStore("button", ButtonElement.class); installStore("slider", SliderElement.class); installStore("textDisplay", TextDisplayElement.class); installStore("light", LightElement.class); } @Override public ModuleElement getModuleStoreById(Object id) { return moduleStoreMap.get(id); } private File cacheDir = null; private File getElementCacheFile() { if (cacheDir != null) return new File(cacheDir, "data.cache"); return null; } private File getImageCacheFile() { if (cacheDir != null) return new File(cacheDir, "image.cache"); return null; } public void setCacheDir(File cacheDir) { this.cacheDir = cacheDir; if (cacheDir != null && (!cacheDir.exists())) { boolean ok = cacheDir.mkdir(); // TODO handle ok==false } } public File getCacheDir() { return cacheDir; } public void parseStore(InputSource source) throws JTException { parseStore(source, null); } public void parseStore(InputSource source, ClassLoader loader) throws JTException { final boolean DEBUG_DISABLE_CACHE = false; if (loader == null) loader = getClass().getClassLoader(); long t = 0; // for timing stuff boolean imageCacheRead = false; boolean elementCacheRead = false; File imageCacheFile = getImageCacheFile(); if (imageCacheFile != null && imageCacheFile.exists() && (!DEBUG_DISABLE_CACHE)) { if (DEBUG) System.out.println(this+": image cache file "+imageCacheFile+" (exists)"); try { if (DEBUG) t = System.currentTimeMillis(); imageCache.readCacheFile(imageCacheFile); imageCacheRead = true; if (DEBUG) System.out.println(this+": image cache read in "+(System.currentTimeMillis()-t)+"ms"); } catch (Throwable et) { if (log.isTraceEnabled()) log.trace("loading images from cache failed", et); } } File cacheFile = getElementCacheFile(); try { if (cacheFile != null && (!DEBUG_DISABLE_CACHE)) { if (DEBUG) System.out.println(this+": element cache file "+cacheFile+" (exists:"+cacheFile.exists()+")"); if (cacheFile.exists()) { if (DEBUG) t = System.currentTimeMillis(); if (initializeFromCache(cacheFile, loader)) { elementCacheRead = true; if (DEBUG) System.out.println(this+": elements initialized from cache in "+(System.currentTimeMillis()-t)+"ms"); } else { if (DEBUG) System.out.println(this+": initialization of elements from cache failed"); } } } } catch (Exception e) { if (log.isTraceEnabled()) log.trace("loading elements from cache failed", e); } if (imageCacheRead && elementCacheRead) return; if (!elementCacheRead) { SAXBuilder saxBuilder = new SAXBuilder(); try { if (DEBUG) t = System.currentTimeMillis(); Document document = saxBuilder.build(source); buildStore(document); if (DEBUG) System.out.println(this+": built store in "+(System.currentTimeMillis()-t)+"ms"); } catch (JDOMException e) { throw new JTException(e); } catch (IOException e) { throw new JTException(e); } if (cacheFile != null && (!DEBUG_DISABLE_CACHE)) { if (DEBUG) System.out.println(this+": writing element cache"); writeCache(cacheFile); } } if (!imageCacheRead) { if (imageCacheFile != null) { // render images if (DEBUG) System.out.println(this+": render images..."); if (DEBUG) t = System.currentTimeMillis(); for (ModuleElement m: moduleStoreMap.values()) { for (ComponentElement e: m) { if (e instanceof ImageElement) ((ImageElement) e).renderImage(this); } } if (DEBUG) System.out.println(this+": rendered in "+(System.currentTimeMillis()-t)+"ms"); try { if (!DEBUG_DISABLE_CACHE) { if (DEBUG) System.out.println(this+": write image cache..."); imageCache.writeCacheFile(imageCacheFile); } } catch (Exception e) { if (log.isWarnEnabled()) log.warn("could not write image cache to: "+imageCacheFile, e); } } } } private void writeCache(File cacheFile) { try { __writeCache(cacheFile); } catch (Exception e) { if (log.isErrorEnabled()) log.error("writeCache("+(cacheFile==null?null:cacheFile.getAbsolutePath())+") failed", e); } } private void __writeCache(File cacheFile) throws Exception { ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(cacheFile))); try { // css out.writeObject(cssStyleSheet); // defs out.writeInt(imageResourceMap.size()); for (Object key: imageResourceMap.keySet()) { out.writeObject(key); Object ir = imageResourceMap.get(key); out.writeObject(ir); } // modules out.writeInt(moduleStoreMap.size()); for (ModuleElement e: moduleStoreMap.values()) { out.writeObject(e); } } finally { out.flush(); out.close(); } } private boolean initializeFromCache(File cacheFile, ClassLoader loader) throws Exception { ObjectInputStream in = new PluginObjectInputStream(loader, new BufferedInputStream(new FileInputStream(cacheFile))); try { { // css cssStyleSheet = (String) in.readObject(); try { buildCssFromString(cssStyleSheet); } finally { cssStyleSheet = null; } } { // defs ClassLoader cl = getContextClassLoader(); if (cl == null) cl = loader; int size = in.readInt(); imageResourceMap = new HashMap<String, ImageResource>(size*2); for (int i=0;i<size;i++) { String key = (String) in.readObject(); ImageResource ir = (ImageResource) in.readObject(); ir.setCustomClassLoader(cl); ir.setImageCache(imageCache); imageResourceMap.put(key, ir); } } { // module elements int size = in.readInt(); moduleStoreMap = new HashMap<Object, ModuleElement>(size); for (int i=0;i<size;i++) { ModuleElement e = (ModuleElement) in.readObject(); moduleStoreMap.put(e.getId(), e); e.initializeElement(this); } } } finally { in.close(); } return true; } /* public static DefaultStorageContext parseStore(ClassLoader classLoader, InputSource source) throws JTException { DefaultStorageContext dsc = new DefaultStorageContext(classLoader); dsc.parseStore(source); return dsc; } */ protected void buildStore(Document document) throws JTException { Element root = document.getRootElement(); buildDefs(root); buildCss(root); buildModules(root); } private void buildDefs(Element root) { Element defs = root.getChild("defs"); if (defs == null) return; List imageList = defs.getChildren("image"); for (int i=imageList.size()-1;i>=0;i--) { buildDefsImage((Element)imageList.get(i)); } } private void buildDefsImage(Element element) { String id = element.getAttributeValue("id"); if (id.length() == 0) return; ImageResource is = ImageElement.getImageResource(this, element); if (is != null) { is.setImageCache(getImageCache()); imageResourceMap.put(id, is); } } public ImageResource getImageResourceById(String id) { return imageResourceMap.get(id); } protected transient String cssStyleSheet; private void buildCss(Element root) throws JTException { Element styleElement = null; List<?> children = root.getChildren(); for (int i=0;i<children.size();i++) { Element e = (Element) children.get(i); String name = e.getName(); if (name.equals("style")) { styleElement = e; break; } else if (!name.equals("defs")) { break; } } cssStyleSheet = styleElement != null ? styleElement.getText() : ""; buildCssFromString(cssStyleSheet); } private void buildCssFromString(String cssText) throws JTException { CSSOMParser cssParser = CSSUtils.getCSSOMParser(); try { // TODO set uri ??? //source.setURI(arg0) if (cssText == null) return; org.w3c.css.sac.InputSource source = new org.w3c.css.sac.InputSource(new StringReader(cssText)); styleSheet = cssParser.parseStyleSheet(source, null, null); } catch (NullPointerException e) { Log log = getLogger(); if (log.isWarnEnabled()) { log.warn("buildCssFromString", e); } } catch (IOException e) { Log log = getLogger(); if (log.isWarnEnabled()) { log.warn("buildCssFromString", e); } return; // throw new JTException(e); } } private void buildModules(Element root) throws JTException { for (Object moduleElement : root.getChildren("module")) { buildModuleStore((Element)moduleElement); } } private void buildModuleStore(Element moduleElement) throws JTException { ModuleElement moduleStore = (ModuleElement) buildComponentStore(moduleElement); for (Object uchild : moduleElement.getChildren()) { Element child = (Element) uchild; String name = child.getName(); boolean dontLoad = name.equals("name") || name.startsWith("select-"); if (!dontLoad) { ComponentElement childStore = tryBuildComponentStore(moduleElement, child); if (childStore != null) { moduleStore.add(childStore); } } } moduleStoreMap.put(moduleStore.getId(), moduleStore); } private ComponentElement tryBuildComponentStore(Element moduleElement, Element element) { try { return buildComponentStore(element); } catch (JTException e) { Log log = getLogger(); if (log.isWarnEnabled()) { log.warn("could not create component store for element "+ element.getName()+ " (parent "+moduleElement.getAttributeValue("component-id")+")", e); } return null; } } private ComponentElement buildComponentStore(Element element) throws JTException { // TODO not all elements can be handled ???? try { return createStore(element); } catch (JTException e) { // TODO find a better solution for unsupported elements if (e.getMessage().startsWith("No store for element")) { Log log = getLogger(); if (log.isWarnEnabled()) { log.warn(this, e); } return null; } throw e; } } }