gt: 			bgColor = ColorUtils.stringToColor(colorString);
context: /*
   *  Freeplane - mind map editor
   *  Copyright (C) 2009 Dimitry Polivaev
   *
   *  This file author is Dimitry Polivaev
   *
   *  This program 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.
   *
   *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package org.freeplane.features.styles;
  
  import java.awt.Color;
  import java.io.File;
  import java.io.IOException;
  import java.io.StringWriter;
  import java.net.URL;
  import java.util.Collections;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Properties;
  import java.util.Vector;
  
  import org.freeplane.core.extension.IExtension;
  import org.freeplane.core.io.IElementContentHandler;
  import org.freeplane.core.io.IElementDOMHandler;
  import org.freeplane.core.io.IElementHandler;
  import org.freeplane.core.io.IExtensionElementWriter;
  import org.freeplane.core.io.ITreeWriter;
  import org.freeplane.core.resources.NamedObject;
  import org.freeplane.core.resources.ResourceController;
  import org.freeplane.core.ui.components.UITools;
  import org.freeplane.core.undo.IActor;
  import org.freeplane.core.undo.IUndoHandler;
  import org.freeplane.core.util.ColorUtils;
  import org.freeplane.core.util.Compat;
  import org.freeplane.core.util.LogUtils;
  import org.freeplane.core.util.TextUtils;
  import org.freeplane.features.filter.FilterController;
  import org.freeplane.features.filter.condition.ASelectableCondition;
  import org.freeplane.features.filter.condition.ConditionFactory;
  import org.freeplane.features.map.IMapLifeCycleListener;
  import org.freeplane.features.map.MapChangeEvent;
  import org.freeplane.features.map.MapController;
  import org.freeplane.features.map.MapModel;
  import org.freeplane.features.map.MapWriter;
  import org.freeplane.features.map.MapWriter.Mode;
  import org.freeplane.features.map.NodeModel;
  import org.freeplane.features.mode.Controller;
  import org.freeplane.features.mode.ModeController;
  import org.freeplane.features.mode.NodeHookDescriptor;
  import org.freeplane.features.mode.PersistentNodeHook;
  import org.freeplane.features.styles.ConditionalStyleModel.Item;
  import org.freeplane.features.url.UrlManager;
  import org.freeplane.n3.nanoxml.XMLElement;
  
  /**
   * @author Dimitry Polivaev
   * Mar 9, 2009
   */
  @NodeHookDescriptor(hookName = "MapStyle")
  public class MapStyle extends PersistentNodeHook implements IExtension, IMapLifeCycleListener {
  	private static final String NODE_CONDITIONAL_STYLES = "NodeConditionalStyles";
  	public static final String RESOURCES_BACKGROUND_COLOR = "standardbackgroundcolor";
  	public static final String RESOURCES_BACKGROUND_IMAGE = "backgroundImageURI";
  	public static final String MAP_STYLES = "MAP_STYLES";
  	public static final String FIT_TO_VIEWPORT = "fit_to_viewport";
  	
  	public static void install(boolean persistent){
  		new MapStyle(persistent);
  	}
  	
  	protected MapStyle( final boolean persistent) {
  		super();
  		ModeController modeController = Controller.getCurrentModeController();
  		if (persistent) {
  			final MapController mapController = modeController.getMapController();
  			mapController.getWriteManager().addExtensionElementWriter(getExtensionClass(),
  				new XmlWriter());
  			mapController.getReadManager().addElementHandler("conditional_styles", new IElementDOMHandler() {
  				public Object createElement(Object parent, String tag, XMLElement attributes) {
  					return parent;
  				}
  
  				public void endElement(Object parent, String tag, Object element, XMLElement dom) {
  					final NodeModel node = (NodeModel) parent;
  					final MapStyleModel mapStyleModel = MapStyleModel.getExtension(node);
  					if(mapStyleModel != null)
  						loadConditionalStyles(mapStyleModel.getConditionalStyleModel(), dom);
  				}
  				});
  
  				mapController.getWriteManager().addExtensionElementWriter(ConditionalStyleModel.class,
  					new IExtensionElementWriter() {
  					public void writeContent(ITreeWriter writer, Object element, IExtension extension) throws IOException {
  						final ConditionalStyleModel conditionalStyleModel = (ConditionalStyleModel) extension;
  						if (conditionalStyleModel.getStyleCount() == 0)
  							return;
  						final XMLElement hook = new XMLElement("hook");
  						hook.setAttribute("NAME", NODE_CONDITIONAL_STYLES);
  						saveConditionalStyles(conditionalStyleModel, hook, false);
  						writer.addElement(null, hook);
  					}
  				});
  				
  				mapController.getReadManager().addElementHandler("hook", new IElementDOMHandler() {
  					public Object createElement(Object parent, String tag, XMLElement attributes) {
  						if (attributes == null 
  								|| !NODE_CONDITIONAL_STYLES.equals(attributes.getAttribute("NAME", null))) {
  							return null;
  					}
  					final ConditionalStyleModel conditionalStyleModel = new ConditionalStyleModel();
  					((NodeModel)parent).addExtension(conditionalStyleModel);
  					return conditionalStyleModel;
  				}
  				
  				public void endElement(Object parent, String tag, Object element, XMLElement dom) {
  					loadConditionalStyles((ConditionalStyleModel) element, dom);
  				}
  			});
  			mapController.getReadManager().addElementHandler("map_styles",  new IElementContentHandler() {
  				public Object createElement(Object parent, String tag, XMLElement attributes) {
  	                return parent;
                  }
  				public void endElement(final Object parent, final String tag, final Object userObject,
  				                       final XMLElement attributes, final String content) {
  					// bugfix
  					if(isContentEmpty(content))
  						return;
  					final NodeModel node = (NodeModel) userObject;
  					final MapStyleModel mapStyleModel = MapStyleModel.getExtension(node);
  					if (mapStyleModel == null) {
  						return;
  					}
  					final MapModel map = node.getMap();
  					mapStyleModel.createStyleMap(map, mapStyleModel, content);
  					map.getIconRegistry().addIcons(mapStyleModel.getStyleMap());
  				}
  				private boolean isContentEmpty(final String content) {
  					return content.indexOf('<') == -1;
  				}
  
  			}
  			);
  
  		}
  		modeController.getMapController().addMapLifeCycleListener(this);
  		final MapController mapController = modeController.getMapController();
  		mapController.addMapLifeCycleListener(this);
  	}
  
  	protected class XmlWriter implements IExtensionElementWriter {
  		public void writeContent(final ITreeWriter writer, final Object object, final IExtension extension)
  		        throws IOException {
  			final MapStyleModel mapStyleModel = (MapStyleModel) extension;
  			final MapModel styleMap = mapStyleModel.getStyleMap();
  			final String el = System.getProperty("line.separator");
  			if (styleMap == null) {
  				return;
  			}
  			final MapWriter mapWriter = Controller.getCurrentModeController().getMapController().getMapWriter();
  			final StringWriter sw = new StringWriter();
  			sw.append(el);
  			sw.append("<map_styles>");
  			sw.append(el);
  			final NodeModel rootNode = styleMap.getRootNode();
  			final boolean forceFormatting = Boolean.TRUE.equals(writer.getHint(MapWriter.WriterHint.FORCE_FORMATTING));
  			try {
  				mapWriter.writeNodeAsXml(sw, rootNode, Mode.STYLE, true, true, forceFormatting);
  			}
  			catch (final IOException e) {
  				e.printStackTrace();
  			}
  			sw.append("</map_styles>");
  			sw.append(el);
  			final XMLElement element = new XMLElement("hook");
  			saveExtension(extension, element);
  			writer.addElement(sw.toString(), element);
  		}
  	}
  
  	@Override
  	protected XmlWriter createXmlWriter() {
  		return null;
  	}
  
  	protected class MyXmlReader extends XmlReader{
  		public Object createElement(final Object parent, final String tag, final XMLElement attributes) {
  			if (null == super.createElement(parent, tag, attributes)){
  				return null;
  			}
  			super.endElement(parent, tag, parent, attributes);
  			return parent;
  		}
  
  		@Override
          public void endElement(Object parent, String tag, Object userObject, XMLElement xml) {
  			// do nothing for not root nodes
  			final XMLElement parentNodeElement = xml.getParent().getParent();
  			if (parentNodeElement == null || !parentNodeElement.getName().equals("map")) {
  				return;
  			}
  			NodeModel node = (NodeModel) userObject;
  			loadMapStyleProperties(MapStyleModel.getExtension(node), xml);
         }
  
  		private void loadMapStyleProperties(MapStyleModel model, XMLElement xml) {
  			final Vector<XMLElement> propertyXml = xml.getChildrenNamed("properties");
  			if(propertyXml != null && propertyXml.size() >= 1){
  				final Map<String, String> properties = model.getProperties();
  				final Properties attributes = propertyXml.get(0).getAttributes();
  				for(Entry<Object, Object> attribute:attributes.entrySet()){
  					properties.put(attribute.getKey().toString(), attribute.getValue().toString());
  				}
  			}
          }
  		
  	}
  
  	@Override
  	protected IElementHandler createXmlReader() {
  		return new MyXmlReader();
  	}
  
  	@Override
  	protected IExtension createExtension(final NodeModel node, final XMLElement element) {
  		final MapStyleModel model = new MapStyleModel();
  		final String colorString = element.getAttribute("background", null);
  		final Color bgColor;
  		if (colorString != null) {
  