package net.osmand.plus.render; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import net.osmand.IProgress; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.render.RenderingRuleProperty; import net.osmand.render.RenderingRulesStorage; import net.osmand.render.RenderingRulesStorage.RenderingRulesStorageResolver; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class RendererRegistry { private final static Log log = PlatformUtil.getLog(RendererRegistry.class); public final static String DEFAULT_RENDER = "OsmAnd"; //$NON-NLS-1$ public final static String DEFAULT_RENDER_FILE_PATH = "default.render.xml"; public final static String TOURING_VIEW = "Touring view (contrast and details)"; //$NON-NLS-1$ public final static String WINTER_SKI_RENDER = "Winter and ski"; //$NON-NLS-1$ public final static String NAUTICAL_RENDER = "Nautical"; //$NON-NLS-1$ public final static String TOPO_RENDER = "Topo"; //$NON-NLS-1$ public final static String MAPNIK_RENDER = "Mapnik"; //$NON-NLS-1$ private RenderingRulesStorage defaultRender = null; private RenderingRulesStorage currentSelectedRender = null; private Map<String, File> externalRenderers = new LinkedHashMap<String, File>(); private Map<String, String> internalRenderers = new LinkedHashMap<String, String>(); private Map<String, RenderingRulesStorage> renderers = new LinkedHashMap<String, RenderingRulesStorage>(); public interface IRendererLoadedEventListener { void onRendererLoaded(String name, RenderingRulesStorage rules, InputStream source); } private IRendererLoadedEventListener rendererLoadedEventListener; private OsmandApplication app; public RendererRegistry(OsmandApplication app){ this.app = app; internalRenderers.put(DEFAULT_RENDER, DEFAULT_RENDER_FILE_PATH); internalRenderers.put(TOURING_VIEW, "Touring-view_(more-contrast-and-details)" +".render.xml"); internalRenderers.put(TOPO_RENDER, "topo" + ".render.xml"); internalRenderers.put(MAPNIK_RENDER, "mapnik" + ".render.xml"); internalRenderers.put("UniRS", "UniRS" + ".render.xml"); internalRenderers.put("LightRS", "LightRS" + ".render.xml"); internalRenderers.put(NAUTICAL_RENDER, "nautical" + ".render.xml"); internalRenderers.put(WINTER_SKI_RENDER, "skimap" + ".render.xml"); } public RenderingRulesStorage defaultRender() { if(defaultRender == null){ defaultRender = getRenderer(DEFAULT_RENDER); } return defaultRender; } public RenderingRulesStorage getRenderer(String name) { if(renderers.containsKey(name)){ return renderers.get(name); } if(!hasRender(name)){ return null; } try { RenderingRulesStorage r = loadRenderer(name, new LinkedHashMap<String, RenderingRulesStorage>(), new LinkedHashMap<String, String>()); renderers.put(name, r); return r; } catch (IOException e) { log.error("Error loading renderer", e); //$NON-NLS-1$ } catch (XmlPullParserException e) { log.error("Error loading renderer", e); //$NON-NLS-1$ } return null; } private boolean hasRender(String name) { return externalRenderers.containsKey(name) || internalRenderers.containsKey(name); } // private static boolean USE_PRECOMPILED_STYLE = false; private RenderingRulesStorage loadRenderer(String name, final Map<String, RenderingRulesStorage> loadedRenderers, final Map<String, String> renderingConstants) throws IOException, XmlPullParserException { // if ((name.equals(DEFAULT_RENDER) || name.equalsIgnoreCase("default")) && USE_PRECOMPILED_STYLE) { // RenderingRulesStorage rrs = new RenderingRulesStorage("", null); // new DefaultRenderingRulesStorage().createStyle(rrs); // log.info("INIT rendering from class"); // return rrs; // } InputStream is = getInputStream(name); if(is == null) { return null; } try { XmlPullParser parser = PlatformUtil.newXMLPullParser(); parser.setInput(is, "UTF-8"); int tok; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { String tagName = parser.getName(); if (tagName.equals("renderingConstant")) { if (!renderingConstants.containsKey(parser.getAttributeValue("", "name"))) { renderingConstants.put(parser.getAttributeValue("", "name"), parser.getAttributeValue("", "value")); } } } } } finally { is.close(); } // parse content is = getInputStream(name); final RenderingRulesStorage main = new RenderingRulesStorage(name, renderingConstants); loadedRenderers.put(name, main); try { main.parseRulesFromXmlInputStream(is, new RenderingRulesStorageResolver() { @Override public RenderingRulesStorage resolve(String name, RenderingRulesStorageResolver ref) throws XmlPullParserException { // reload every time to propogate rendering constants if (loadedRenderers.containsKey(name)) { log.warn("Circular dependencies found " + name); //$NON-NLS-1$ } RenderingRulesStorage dep = null; try { dep = loadRenderer(name, loadedRenderers, renderingConstants); } catch (IOException e) { log.warn("Dependent renderer not found : " + e.getMessage(), e); //$NON-NLS-1$ } if (dep == null) { log.warn("Dependent renderer not found : " + name); //$NON-NLS-1$ } return dep; } }); } finally { is.close(); } if (rendererLoadedEventListener != null) rendererLoadedEventListener.onRendererLoaded(name, main, getInputStream(name)); return main; } public InputStream getInputStream(String name) throws FileNotFoundException { InputStream is; if("default".equalsIgnoreCase(name)) { name = DEFAULT_RENDER; } if(externalRenderers.containsKey(name)){ is = new FileInputStream(externalRenderers.get(name)); } else if(internalRenderers.containsKey(name)){ File fl = getFileForInternalStyle(name); if(fl.exists()) { is = new FileInputStream(fl); } else { copyFileForInternalStyle(name); is = RenderingRulesStorage.class.getResourceAsStream(internalRenderers.get(name)); } } else { throw new IllegalArgumentException("Not found " + name); //$NON-NLS-1$ } return is; } public void copyFileForInternalStyle(String name) { try { FileOutputStream fout = new FileOutputStream(getFileForInternalStyle(name)); Algorithms.streamCopy(RenderingRulesStorage.class.getResourceAsStream(internalRenderers.get(name)), fout); fout.close(); } catch (IOException e) { log.error(e.getMessage(), e); } } public Map<String, String> getInternalRenderers() { return internalRenderers; } public File getFileForInternalStyle(String name) { if(!internalRenderers.containsKey(name)) { return new File(app.getAppPath(IndexConstants.RENDERERS_DIR), "style.render.xml"); } File fl = new File(app.getAppPath(IndexConstants.RENDERERS_DIR), internalRenderers.get(name)); return fl; } public void initRenderers(IProgress progress) { File file = app.getAppPath(IndexConstants.RENDERERS_DIR); file.mkdirs(); Map<String, File> externalRenderers = new LinkedHashMap<String, File>(); if (file.exists() && file.canRead()) { File[] lf = file.listFiles(); if (lf != null) { for (File f : lf) { if (f != null && f.getName().endsWith(IndexConstants.RENDERER_INDEX_EXT)) { if(!internalRenderers.containsValue(f.getName())) { String name = f.getName().substring(0, f.getName().length() - IndexConstants.RENDERER_INDEX_EXT.length()); externalRenderers.put(name.replace('_', ' ').replace('-', ' '), f); } } } } } this.externalRenderers = externalRenderers; String r = app.getSettings().RENDERER.get(); if(r != null){ RenderingRulesStorage obj = getRenderer(r); if(obj != null){ setCurrentSelectedRender(obj); } } } public Collection<String> getRendererNames(){ LinkedHashSet<String> names = new LinkedHashSet<String>(); names.add(DEFAULT_RENDER); names.addAll(internalRenderers.keySet()); names.addAll(externalRenderers.keySet()); return names; } public RenderingRulesStorage getCurrentSelectedRenderer() { if(currentSelectedRender == null){ return defaultRender(); } return currentSelectedRender; } public void setCurrentSelectedRender(RenderingRulesStorage currentSelectedRender) { this.currentSelectedRender = currentSelectedRender; } public void setRendererLoadedEventListener(IRendererLoadedEventListener listener) { rendererLoadedEventListener = listener; } public IRendererLoadedEventListener getRendererLoadedEventListener() { return rendererLoadedEventListener; } public RenderingRuleProperty getCustomRenderingRuleProperty(String attrName) { RenderingRulesStorage renderer = getCurrentSelectedRenderer(); if (renderer != null) { for (RenderingRuleProperty p : renderer.PROPS.getCustomRules()) { if (p.getAttrName().equals(attrName)) { return p; } } } return null; } }