public interface Styleable {
    /**
     * Returns the ID of the object, as used in CSS ID selectors.  ID selectors
     * are of the form <code>#idstring</code> and match all objects with the 
     * specified ID.
     *
     *@return the object's ID
     */
    String getID();
    
    /**
     * Returns an array of all Java classes which should be considered matches 
     * for this object.  Normally this includes the base object class as well as 
     * all implemented interfaces.
     *
     *@return the Java classes of the object being styled
     */
    Class[] getObjectClasses();
    
    /**
     * Returns the style class of the object, as used in CSS class selectors.  
     * Class selectors are of the form <code>.style</code> and match all objects 
     * with the specified style class.
     *
     *@return the style class
     */
    String getStyleClass();
    
    /**
     * Informs the object that a new stylesheet is being applied to it.  The 
     * <code>depth</code> parameter indicates the "ancestry level" of the style 
     * root (the object to which the stylesheet is being directly applied);  for 
     * instance if the style root is this object's parent, the depth is 1.  The 
     * depth is needed to correctly resolve priorities in the face of 
     * conflicting rules, with lower depths having precedence over higher
     * depths.  Generally the depth is only useful as an argument to various 
     * methods in {@link PropertyManager} which perform these priority 
     * resolutions.
     * <p>
     * Objects are not required to do anything in response to this method; it is 
     * largely informational.  {@link ComponentStyleSupport} keeps track of 
     * stylesheets affecting it so that newly-added children can be styled 
     * automatically.
     *
     *@param s the stylesheet being applied
     *@param depth the depth of this object below the style root
     *@see #removeStylesheet
     */
    void addStylesheet(Stylesheet s, int depth);

    /**
     * Informs the object that a stylesheet no longer applies to it. The 
     * <code>depth</code> parameter indicates the "ancestry level" of the style 
     * root (the object to which the stylesheet is being directly applied);  for 
     * instance if the style root is this object's parent, the depth is 1.  The 
     * depth is needed to correctly resolve priorities in the face of 
     * conflicting rules, with lower depths having precedence over higher 
     * depths.  Generally the depth is only useful as an argument to various 
     * methods in {@link PropertyManager} which perform these priority 
     * resolutions.
     * <p>
     * Objects are not required to do anything in response to this method; it is 
     * largely informational.  {@link ComponentStyleSupport} keeps track of
     * stylesheets affecting it so that newly-added children can be styled 
     * automatically.
     *
     *@param s the stylesheet being applied
     *@param depth the depth of this object below the style root
     *@see #addStylesheet
     */
    void removeStylesheet(Stylesheet s, int depth);

    /**
     * Converts a property from a string representation (as specified in a CSS 
     * document) to its actual value.  Styleable objects should generally 
     * determine the type of the property and then use {@link 
     * com.sun.css.types.TypeManager TypeManager} to perform the actual 
     * conversion.
     *
     *@param propertyName the name of the property being assigned
     *@param value a string representation of the data as specified in a CSS 
     *      document
     *@return the converted value
     *@throws StylesheetException if the value could not be converted
     */
    Object convertPropertyFromString(String propertyName, String value) 
            throws StylesheetException;

    /**
     * Returns the current value of the specified property.
     *
     *@param key the name of the property to return
     *@return the value of the property
     *@throws StylesheetException if the property does not exist or could not be read
     *@see #setProperty
     */
    Object getProperty(String key) throws StylesheetException;

    /**
     * Changes a property of the object.  <code>setProperty</code> should 
     * normally only be called directly by {@link com.sun.css.PropertyManager} 
     * so as to correctly handle priority resolution and stylesheet removal.
     *
     *@param key the name of the property to set
     *@param value the new value of the property
     *@throws StylesheetException if the property does not exist or could not be
     *      written
     *@see #getProperty
     */
    void setProperty(String key, Object value) throws StylesheetException;
    
    /**
     * Returns the children of this object.  When <code>Styleable</code> 
     * wrappers are used, it is essential that the same wrapper instance always 
     * be used for the same object.  {@link
     * com.sun.css.types.TypeManager#getStyleable TypeManager.getStyleable()} is 
     * the preferred way to obtain <code>Styleable</code> wrappers, as it 
     * automatically ensures that the same wrapper is reused for the same 
     * object.
     *
     *@return an array of the object's children, which may be <code>null</code>
     */
    Styleable[] getStyleableChildren();
    
    /**
     * Returns the parent of this object.  When <code>Styleable</code> wrappers 
     * are used, it is essential that the same wrapper instance always be used 
     * for the same object.  {@link
     * com.sun.css.types.TypeManager#getStyleable TypeManager.getStyleable()} is 
     * the preferred way to obtain <code>Styleable</code> wrappers, as it 
     * automatically ensures that the same wrapper is reused for the same 
     * object.
     *
     *@return this object's parent, which may be <code>null</code>
     */
    Styleable getStyleableParent();
    
    /**
     * Adds a listener which will be notified as the specified pseudoclass is 
     * added to or removed from this object.  For example, {@link 
     * java.awt.Component Components} support the <code>mouseover</code> 
     * pseudoclass, and adding a <code>PseudoclassListener</code> with the
     * <code>mouseover</code> pseudoclass will cause it to receive notifications
     * as the mouse enters and exits the component.
     * <p>
     * <b>Important:</b> if the pseudoclass is <i>already in effect</i> when
     * this method is called (i.e. adding a listener for "enabled" to a button
     * which is currently enabled) this method is responsible for immediately
     * calling <code>pseudoclassAdded()</code> on the listener.
     *
     *@param pseudoclass the pseudoclass to listen for
     *@param listener the listener which will receive pseudoclass notifications
     *@throws StylesheetException if the specified pseudoclass is not supported by this 
     *      object
     *@see #removePseudoclassListener
     */
    void addPseudoclassListener(String pseudoclass, 
            PseudoclassListener listener) throws StylesheetException;

    /**
     * Removes a pseudoclass listener from this object.
     *
     *@param pseudoclass the pseudoclass which was being listened for
     *@param listener the listener to remove
     *@throws StylesheetException if the specified pseudoclass is not supported by this
     *      object
     *@see #addPseudoclassListener
     */
    void removePseudoclassListener(String pseudoclass, 
            PseudoclassListener listener) throws StylesheetException;

    /**
     * Returns <code>true</code> if the specified property should be inherited 
     * by this object when it is found on this object's parent.
     *
     *@param propertyName the name of the property to potentially inherit
     *@return <code>true</code> if the property should be inherited
     *@throws StylesheetException if an error occurs
     */
    boolean isPropertyInherited(String propertyName) throws StylesheetException;
    
    
    /**
     * Attempts to split a 'compound' property into its component pieces.  A 
     * compound property is one such as "font", which is split into "font-size", 
     * "font-weight", "font-face", and "font-style".  When queried with a 
     * compound property, this method returns a <code>Map</code> of the 
     * individual properties to their values.  When queried with a simple 
     * (non-compound) property, this method returns <code>null</code>.
     *
     *@param propertyName the name of the property to split
     *@param value the property's value
     *@return a <code>Map</code> of the individual properties and values, or 
     *      <code>null</code> for non-compound properties
     *@throws StylesheetException if an error occurs
     */
    Map<String, Object> splitCompoundProperty(String propertyName, Object value)
            throws StylesheetException;
} public class StylesheetException extends RuntimeException {
    /** Creates a new <code>StylesheetException</code>. */
    public StylesheetException() {
    }
    
    
    /** 
     * Creates a new <code>StylesheetException</code> with the specified detail 
     * message. 
     *
     *@param msg the exception's detail message
     */
    public StylesheetException(String msg) {
        super(msg);
    }

 
    /**
     * Creates a new <code>StylesheetException</code> with the specified cause.
     *
     *@param initCause the exception's initCause
     */
    public StylesheetException(Throwable initCause) {
        super(initCause);
    }


    /**
     * Creates a new <code>StylesheetException</code> with the specified detail 
     * message and cause.
     *
     *@param msg the exception's detail message
     *@param initCause the exception's initCause
     */
    public StylesheetException(String msg, Throwable initCause) {
        super(msg, initCause);
    }
} public class DefaultStyleable implements Styleable {
	private static class StylesheetApplication {
		private Stylesheet stylesheet;

		private int depth;

		private StylesheetApplication(Stylesheet stylesheet, int depth) {
			this.stylesheet = stylesheet;
			this.depth = depth;
		}

		public boolean equals(Object o) {
			if (!(o instanceof StylesheetApplication))
				return false;

			StylesheetApplication s = (StylesheetApplication) o;
			return s.stylesheet == stylesheet && s.depth == depth;
		}

		public int hashCode() {
			return stylesheet.hashCode() ^ depth;
		}
	}

	// static Map<Class, StyleSupport> support = new HashMap<Class,
	// StyleSupport>();

	Object object;

	Set<StylesheetApplication> stylesheets = new HashSet<StylesheetApplication>();

	public DefaultStyleable(Object object) {
		this.object = object;
		getStyleSupport().addHierarchyListener(this);
	}

	/**
	 * Informs the object that a new stylesheet is being applied to it. The
	 * <code>depth</code> parameter indicates the "ancestry level" of the style
	 * root (the object to which the stylesheet is being directly applied); for
	 * instance if the style root is this object's parent, the depth is 1. The
	 * depth is needed to correctly resolve priorities in the face of
	 * conflicting rules, with lower depths having precedence over higher
	 * depths.
	 * <p>
	 * Knowing which stylesheets are affecting it allows
	 * <code>DefaultStyleable</code> to automatically apply stylesheets to
	 * newly-added children, for objects which provide the necessary
	 * notifications.
	 * 
	 *@param s
	 *            the stylesheet being applied
	 *@param depth
	 *            the depth of this object below the style root
	 *@see #removeStylesheet
	 */
	public void addStylesheet(Stylesheet s, int depth) {
		stylesheets.add(new StylesheetApplication(s, depth));
	}

	/**
	 * Informs the object that a stylesheet no longer applies to it. The
	 * <code>depth</code> parameter indicates the "ancestry level" of the style
	 * root (the object to which the stylesheet is being directly applied); for
	 * instance if the style root is this object's parent, the depth is 1. The
	 * depth is needed to correctly resolve priorities in the face of
	 * conflicting rules, with lower depths having precedence over higher
	 * depths.
	 * <p>
	 * Knowing which stylesheets are affecting it allows
	 * <code>DefaultStyleable</code> to automatically apply stylesheets to
	 * newly-added children, for objects which provide the necessary
	 * notifications.
	 * 
	 *@param s
	 *            the stylesheet being removed
	 *@param depth
	 *            the depth of this object below the style root
	 *@see #addStylesheet
	 */
	public void removeStylesheet(Stylesheet s, int depth) {
		stylesheets.remove(new StylesheetApplication(s, depth));
	}

	private synchronized StyleSupport getStyleSupport() {
		return TypeManager.getStyleSupport(object);
	}

	public String getID() {
		return getStyleSupport().getID(object);
	}

	public Object getBaseObject() {
		return object;
	}

	public Class[] getObjectClasses() {
		return getStyleSupport().getObjectClasses(object);
	}

	public String getStyleClass() {
		return getStyleSupport().getStyleClass(object);
	}

	public Object convertPropertyFromString(String propertyName, String value)
			throws StylesheetException {
		Class type = getStyleSupport().getPropertyType(object, propertyName);
		return TypeManager.convertFromString(value, type);
	}

	public Object getProperty(String key) throws StylesheetException {
		return getStyleSupport().getProperty(object, key);
	}

	public void setProperty(String key, Object value) throws StylesheetException {
		getStyleSupport().setProperty(object, key, value);
	}

	public Styleable getStyleableParent() {
		return getStyleSupport().getStyleableParent(object);
	}

	public Styleable[] getStyleableChildren() {
		return getStyleSupport().getStyleableChildren(object);
	}

	public boolean isPropertyInherited(String propertyName) throws StylesheetException {
		return getStyleSupport().isPropertyInherited(object, propertyName);
	}

	public void addPseudoclassListener(String pseudoclass, PseudoclassListener listener)
			throws StylesheetException {
		getStyleSupport().addPseudoclassListener(this, pseudoclass, listener);
	}

	public void removePseudoclassListener(String pseudoclass, PseudoclassListener listener)
			throws StylesheetException {
		getStyleSupport().removePseudoclassListener(this, pseudoclass, listener);
	}

	public void childAdded(Styleable child) {
		try {
			for (StylesheetApplication s : stylesheets)
				s.stylesheet.applyTo(child, s.depth + 1);
			PropertyManager.cascadeTo(child, true);
		} catch (StylesheetException ex) {
			throw new RuntimeException(ex);
		}
	}

	public void childRemoved(Styleable child) {
		try {
			PropertyManager.removeAllStyles(child);
		} catch (StylesheetException ex) {
			throw new RuntimeException(ex);
		}
	}

	public Map<String, Object> splitCompoundProperty(String property, Object value)
			throws StylesheetException {
		return getStyleSupport().splitCompoundProperty(object, property, value);
	}

	public boolean equals(Object o) {
		if (!(o instanceof DefaultStyleable))
			return false;
		return object == ((DefaultStyleable) o).object;
	}

	public int hashCode() {
		return object.hashCode();
	}

	public String toString() {
		return "DefaultStyleable[" + object.getClass().getName() + ", "
			+ System.identityHashCode(object) + ", " + System.identityHashCode(this) + "]";
	}
} public interface StyleSupport {
    String getID(Object object);
    
    String getStyleClass(Object object);
    
    Class[] getObjectClasses(Object object);
    
    Styleable getStyleableParent(Object object);
    
    Styleable[] getStyleableChildren(Object object);
    
    Class getPropertyType(Object object, String propertyName);
    
    Object getProperty(Object object, String propertyName);
    
    void setProperty(Object object, String propertyName, Object value);
    
    void addPseudoclassListener(DefaultStyleable object, String pseudoclass, 
            PseudoclassListener listener);

    void removePseudoclassListener(DefaultStyleable object, String pseudoclass, 
            PseudoclassListener listener);
    
    Map<String, Object> splitCompoundProperty(Object object, String property, 
                Object value);

    boolean isPropertyInherited(Object object, String propertyName);
    
    PropertyHandler getPropertyHandler(String property);
    
    void addHierarchyListener(DefaultStyleable object);
} public class AbstractButtonStyleSupport extends ComponentStyleSupport {
    private Map<PseudoclassListener, ChangeListener> armedListeners;
   
   
    /**
     * Creates a new <code>AbstractButtonStyleSupport</code> for the specified 
     * class.
     *
     *@param cls the specific button class to support
     */
    public AbstractButtonStyleSupport(Class cls) {
        super(cls);
    }
    
    
    protected void init() throws IntrospectionException {
        super.init();
        properties.put("text-decoration", new TextDecorationHandler(this));
    }
    
    
    protected void createPropertyHandler(PropertyDescriptor descriptor) {
        String name = descriptor.getName();
        if (name.equals("text"))
            properties.put(name, new TextHandler(this, descriptor));
        else
            super.createPropertyHandler(descriptor);
    }
    
    
    private synchronized ChangeListener getArmedListener(
            final PseudoclassListener listener, 
            final PseudoclassEvent event, 
            final boolean inverse) {
        if (armedListeners == null)
            armedListeners = new HashMap<PseudoclassListener, ChangeListener>();
        ChangeListener result = armedListeners.get(listener);
        if (result == null) {
            result = new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    AbstractButton button = (AbstractButton) e.getSource();
                    if (inverse != button.getModel().isArmed())
                        listener.pseudoclassAdded(event);
                    else
                        listener.pseudoclassRemoved(event);
                }
            };
            armedListeners.put(listener, result);
        }
        return result;
    }


    public void addPseudoclassListener(DefaultStyleable styleable, 
            String pseudoclass, final PseudoclassListener listener) 
            throws StylesheetException {
        final PseudoclassEvent event = new PseudoclassEvent(styleable, 
                pseudoclass);
        if (pseudoclass.equals("armed") || pseudoclass.equals("unarmed")) {
            boolean inverse = pseudoclass.equals("unarmed");
            AbstractButton button = (AbstractButton) styleable.getBaseObject();
            if (button.getModel().isArmed() != inverse)
                listener.pseudoclassAdded(event);
            button.addChangeListener(getArmedListener(listener, event, 
                    inverse));
        }
        else
            super.addPseudoclassListener(styleable, pseudoclass, listener);
    }
    
    
    public void removePseudoclassListener(DefaultStyleable styleable, 
            String pseudoclass, final PseudoclassListener listener) 
            throws StylesheetException {
        if (pseudoclass.equals("armed") || pseudoclass.equals("unarmed")) {
            boolean inverse = pseudoclass.equals("unarmed");
            AbstractButton button = (AbstractButton) styleable.getBaseObject();
            button.removeChangeListener(getArmedListener(listener, null, 
                    inverse));
        }
        else
            super.removePseudoclassListener(styleable, pseudoclass, listener);
    }
} public class BorderConverter implements TypeConverter<Border> {
    private Pattern pattern = Pattern.compile("(?:BorderFactory\\.create)?" + 
            "(\\w+)\\s*(?:\\((.*)\\))?");
    private Method[] methods;

    public Border convertFromString(String string) {
        if (string.equals("null"))
            return null;
        try {
            Matcher m = pattern.matcher(string);
            if (m.matches()) {
                String methodName = m.group(1);
                if (methodName.length() > 1) {
                    methodName = "create" + 
                            Character.toUpperCase(methodName.charAt(0)) + 
                            methodName.substring(1);
                    String[] args;
                    String argList = m.group(2);
                    if (argList != null)
                        args = TypeManager.parseArgs(argList);
                    else
                        args = new String[0];
                    if (methods == null)
                        methods = BorderFactory.class.getMethods();
                    for (int i = 0; i < methods.length; i++) {
                        Class[] parameterTypes = methods[i].getParameterTypes();
                        if (methods[i].getName().equals(methodName) &&
                                parameterTypes.length == args.length) {
                            Object[] convertedArgs = new Object[args.length];
                            for (int j = 0; j < args.length; j++)
                                convertedArgs[j] = 
                                        TypeManager.convertFromString(args[j], 
                                        parameterTypes[j]);
                            return (Border) methods[i].invoke(null, 
                                    convertedArgs);
                        }
                    }
                    throw new IllegalArgumentException("BorderFactory does" +
                            "not have a method named " + methodName + 
                            " which takes " + args.length + " arguments");
                }
            }
            throw new IllegalArgumentException("cannot convert '" + string + 
                    "' to type Border");
        }
        catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
} public class ComponentStyleSupport extends BeanStyleSupport {
    static Class jsgPanel;
    static Method getScene;
    static Method getFxComponentFor;
    
    static {
        try {
            Class fxComponent = Class.forName("javafx.gui.Component");
            getFxComponentFor = fxComponent.getMethod("getComponentFor", 
                    JComponent.class);
        }
        catch (ClassNotFoundException e) {
            // ignore
        }
        catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        
        try {
            jsgPanel = Class.forName("com.sun.scenario.scenegraph.JSGPanel");
            getScene = jsgPanel.getMethod("getScene");
        }
        catch (ClassNotFoundException e) {
            // ignore
        }
        catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
    
    
    /** Maps PseudoclassListeners to their mouse over adapters. */
    private Map<PseudoclassListener, MouseListener> mouseOverListeners;

    /** Maps PseudoclassListeners to their mouse down adapters. */
    private Map<PseudoclassListener, MouseListener> mouseDownListeners;


    /** Maps PseudoclassListeners to their FocusListener adapters. */
    private Map<PseudoclassListener, FocusListener> focusListeners;
   
   
    /**
     * Creates a new <code>ComponentStyleSupport</code> for the specified class.
     *
     *@param cls the specific component class to support
     */
    public ComponentStyleSupport(Class cls) {
        super(cls);
    }
    

    /**
     * Returns the value of {@link Component#getName}.
     *
     *@param object the object whose ID is being determined
     *@return the object's ID
     */
    public String getID(Object object) {
        return ((Component) object).getName();
    }
 

    /**
     * Returns the component's style class.  {@link Component Components} do not 
     * support style classes and so return <code>null</code>, but {@link 
     * JComponent JComponents} return the value of 
     * <code>getClientProperty(Stylesheet.STYLE_CLASS_KEY)</code>.
     *
     *@param object the object whose style class is being determined
     *@return the object's style clas
     */
    public String getStyleClass(Object object) {
        if (object instanceof JComponent)
            return (String) ((JComponent) object).getClientProperty(
                    Stylesheet.STYLE_CLASS_KEY);
        else
            return null;
    }
    
    
    public Class[] getObjectClasses(Object object) {
        Class[] result = super.getObjectClasses(object);
        if (object instanceof JComponent && getFxComponentFor != null) {
            try {
                Object fxObject = getFxComponentFor.invoke(null, object);
                if (fxObject != null) {
                    Method getRootJComponent = fxObject.getClass().getMethod(
                            "getRootJComponent");
                    if (getRootJComponent.invoke(fxObject) == object) {
                        Class[] tmp = result;
                        Class[] interfaces = 
                                fxObject.getClass().getInterfaces();
                        result = new Class[tmp.length + interfaces.length + 1];
                        System.arraycopy(tmp, 0, result, 0, tmp.length);
                        System.arraycopy(interfaces, 0, result, tmp.length, 
                                interfaces.length);
                        result[result.length - 1] = fxObject.getClass();
                    }
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }
    
    
    /**
     * Returns the component's parent wrapped in a <code>Styleable</code>.
     *
     *@param object the object whose parent is being determined
     *@return the object's parent
     */
    public Styleable getStyleableParent(Object object) {
        Container parent = ((Component) object).getParent();
        return parent != null ? TypeManager.getStyleable(parent) : null;
    }


    /**
     * Returns the component's children wrapped in a <code>Styleable</code>.
     *
     *@param object the object whose children are being determined
     *@return the object's children
     */
    public Styleable[] getStyleableChildren(Object object) {
        if (jsgPanel != null && jsgPanel.isInstance(object)) {
            try {
                return new Styleable[] { 
                        TypeManager.getStyleable(getScene.invoke(object)) };
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        else if (object instanceof Container) {
            Container c = (Container) object;
            Styleable[] result = new Styleable[c.getComponentCount()];
            for (int i = 0; i < result.length; i++)
                result[i] = TypeManager.getStyleable(c.getComponent(i));
            return result;
        }
        else
            return new Styleable[0];
    }

    @Override
    public void addHierarchyListener(final DefaultStyleable stylable) {
        Object o = stylable.getBaseObject();
        if (o instanceof Container) {
            ((Container) o).addContainerListener(new ContainerListener() {
                public void componentAdded(ContainerEvent e) {
                    Styleable object = TypeManager.getStyleable(e.getChild());
                    stylable.childAdded(object);
                }

                public void componentRemoved(ContainerEvent e) {
                    Styleable object = TypeManager.getStyleable(e.getChild());
                    stylable.childRemoved(object);
                }
            });
        }
    }

    private synchronized FocusListener getFocusListener(
            final PseudoclassListener listener, 
            final PseudoclassEvent event, 
            final boolean inverse) {
        if (focusListeners == null)
            focusListeners = new HashMap<PseudoclassListener, FocusListener>();
        FocusListener result = focusListeners.get(listener);
        if (result == null) {
            result = new FocusListener() {
                public void focusGained(FocusEvent e) {
                    if (inverse)
                        listener.pseudoclassRemoved(event);
                    else
                        listener.pseudoclassAdded(event);
                }
    
    
                public void focusLost(FocusEvent e) {
                    if (inverse)
                        listener.pseudoclassAdded(event);
                    else
                        listener.pseudoclassRemoved(event);
                }
            };
            focusListeners.put(listener, result);
        }
        return result;
    }


    private synchronized MouseListener getMouseOverListener(
            final PseudoclassListener listener, 
            final PseudoclassEvent event, 
            final boolean inverse) {
        if (mouseOverListeners == null)
            mouseOverListeners = 
                    new HashMap<PseudoclassListener, MouseListener>();
        MouseListener result = mouseOverListeners.get(listener);
        if (result == null) {
            result = new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent e) {
                    if (inverse)
                        listener.pseudoclassRemoved(event);
                    else
                        listener.pseudoclassAdded(event);
                }
    
    
                @Override
                public void mouseExited(MouseEvent e) {
                    if (inverse)
                        listener.pseudoclassAdded(event);
                    else
                        listener.pseudoclassRemoved(event);
                }
            };
            mouseOverListeners.put(listener, result);
        }
        return result;
    }
    
    
    private synchronized MouseListener getMouseDownListener(
            final PseudoclassListener listener, 
            final PseudoclassEvent event, 
            final boolean inverse) {
        if (mouseDownListeners == null)
            mouseDownListeners = 
                    new HashMap<PseudoclassListener, MouseListener>();
        MouseListener result = mouseDownListeners.get(listener);
        if (result == null) {
            result = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    if (inverse)
                        listener.pseudoclassRemoved(event);
                    else
                        listener.pseudoclassAdded(event);
                }
    
    
                @Override
                public void mouseReleased(MouseEvent e) {
                    if (inverse)
                        listener.pseudoclassAdded(event);
                    else
                        listener.pseudoclassRemoved(event);
                }
            };
            mouseDownListeners.put(listener, result);
        }
        return result;
    }
    
    
    public void addPseudoclassListener(DefaultStyleable styleable, 
            String pseudoclass, final PseudoclassListener listener) 
            throws StylesheetException {
        final PseudoclassEvent event = new PseudoclassEvent(styleable, 
                pseudoclass);
        Component component = (Component) styleable.getBaseObject();
        if (pseudoclass.equals("focused") || pseudoclass.equals("unfocused")) {
            boolean inverse = pseudoclass.equals("unfocused");
            if (component.hasFocus() != inverse)
                listener.pseudoclassAdded(event);
            component.addFocusListener(getFocusListener(listener, event, 
                    inverse));
        }
        else if (pseudoclass.equals("mouseover") || 
                pseudoclass.equals("mouseout")) {
            boolean inverse = pseudoclass.equals("mouseout");
            if (inverse)
                listener.pseudoclassAdded(event);
            component.addMouseListener(getMouseOverListener(listener, event, 
                    inverse));
        }
        else if (pseudoclass.equals("mousedown") || 
                pseudoclass.equals("mouseup")) {
            boolean inverse = pseudoclass.equals("mouseup");
            if (inverse)
                listener.pseudoclassAdded(event);
            component.addMouseListener(getMouseDownListener(listener, event, 
                    inverse));
        }
        else
            super.addPseudoclassListener(styleable, pseudoclass, listener);
    }
    
    
    public void removePseudoclassListener(DefaultStyleable styleable, 
            String pseudoclass, final PseudoclassListener listener) 
            throws StylesheetException {
        Component component = (Component) styleable.getBaseObject();
        if (pseudoclass.equals("focused") || pseudoclass.equals("unfocused")) {
            boolean inverse = pseudoclass.equals("unfocused");
            component.removeFocusListener(getFocusListener(listener, null, 
                    inverse));
        }
        else if (pseudoclass.equals("mouseover") || 
                pseudoclass.equals("mouseout")) {
            boolean inverse = pseudoclass.equals("mouseout");
            component.removeMouseListener(getMouseOverListener(listener, null, 
                    inverse));
        }
        else if (pseudoclass.equals("mousedown") || 
                pseudoclass.equals("mouseup")) {
            boolean inverse = pseudoclass.equals("mouseup");
            component.removeMouseListener(getMouseDownListener(listener, null, 
                    inverse));
        }
        else
            super.removePseudoclassListener(styleable, pseudoclass, listener);
    }
} public class DimensionConverter implements TypeConverter<Dimension> {
    private Pattern pattern = Pattern.compile("(?:new\\s+Dimension\\s*" +
            "\\(\\s*)?(\\d+)\\s*,\\s*(\\d+)\\s*\\)?");


    public Dimension convertFromString(String string) {
        if (string.equals("null"))
            return null;
        Matcher m = pattern.matcher(string);
        if (m.matches()) {
            return new Dimension(Integer.parseInt(m.group(1)),
                              Integer.parseInt(m.group(2)));
        }
        else
            throw new IllegalArgumentException("unable to convert string '" + 
                    string + "' to Dimension");
    }
} public class FontConverter implements TypeConverter<Font> {
    public Font convertFromString(String string) {
        if (string.equals("null"))
            return null;
        return Font.decode(string);
    }
} public class InsetsConverter implements TypeConverter<Insets> {
    private Pattern pattern = Pattern.compile("(new\\s+Insets\\s*\\(\\s*)?" +
            "(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)?");


    public Insets convertFromString(String string) {
        if (string.equals("null"))
            return null;
        Matcher m = pattern.matcher(string);
        if (m.matches()) {
            return new Insets(Integer.parseInt(m.group(2)),
                              Integer.parseInt(m.group(3)),
                              Integer.parseInt(m.group(4)),
                              Integer.parseInt(m.group(5)));
        }
        else
            throw new IllegalArgumentException("unable to convert string '" +   
                    string + "' to Insets");
    }
} public class JLabelStyleSupport extends ComponentStyleSupport {
    /**
     * Creates a new <code>JLabelStyleSupport</code> for the specified class.
     *
     *@param cls the specific subclass of JLabel to support
     */
    public JLabelStyleSupport(Class cls) {
        super(cls);
    }
    
    
    protected void init() throws IntrospectionException {
        super.init();
        properties.put("text-decoration", new TextDecorationHandler(this));
    }
    
    
    protected void createPropertyHandler(PropertyDescriptor descriptor) {
        String name = descriptor.getName();
        if (name.equals("text"))
            properties.put(name, new TextHandler(this, descriptor));
        else
            super.createPropertyHandler(descriptor);
    }
} public class KeyStrokeConverter implements TypeConverter<KeyStroke> {
    public KeyStroke convertFromString(String string) {
        if (string.equals("null"))
            return null;
        return KeyStroke.getKeyStroke(string);
    }
} public class TextDecorationConverter extends EnumConverter {
    public TextDecorationConverter() {
        super(TextDecorationHandler.Decoration.class);
    }


    public Object convertFromString(String string) {
        if (string.equals("line-through"))
            string = "linethrough";
        return super.convertFromString(string);
    }
} public class TextDecorationHandler implements PropertyHandler {
    public enum Decoration { none, underline, linethrough };
    
    private static final StringBuilder TEXT_DECORATION_KEY = 
            new StringBuilder("$cssTextDecoration");
    private static final String UNDERLINE_START = "<html><!-- css --><u>";
    private static final String UNDERLINE_END = "</u>";
    private static final String LINETHROUGH_START = 
            "<html><!-- css --><strike>";
    private static final String LINETHROUGH_END = "</strike>";

    private StyleSupport styleSupport;
    private TextHandler textHandler;

    
    public TextDecorationHandler(StyleSupport styleSupport) {
        this.styleSupport = styleSupport;
    }


    public Class getPropertyType(Object object) {
        return Decoration.class;
    }
    
    
    private String stripText(String text) {
        if (text.startsWith(UNDERLINE_START) && text.endsWith(UNDERLINE_END))
            text = text.substring(UNDERLINE_START.length(), text.length() - 
                    UNDERLINE_END.length());
        if (text.startsWith(LINETHROUGH_START) && 
                text.endsWith(LINETHROUGH_END))
            text = text.substring(LINETHROUGH_START.length(), text.length() - 
                    LINETHROUGH_END.length());
        return text;
    }
    
    
    TextHandler getTextHandler() {
        if (textHandler == null)
            textHandler = (TextHandler) styleSupport.getPropertyHandler("text");
        return textHandler;
    }


    public Object getProperty(Object object) throws StylesheetException {
        JComponent c = (JComponent) object;
        Decoration result = (Decoration) 
                c.getClientProperty(TEXT_DECORATION_KEY);
        return result != null ? result : Decoration.none;
    }
    
    
    public void setProperty(Object object, Object value) throws StylesheetException {
        ((JComponent) object).putClientProperty(TEXT_DECORATION_KEY, value);
        String originalText = (String) getTextHandler().getProperty(object);
        if (originalText != null) {
            String newText = stripText(originalText);
            if (value == Decoration.underline)
                newText = UNDERLINE_START + newText + UNDERLINE_END;
            else if (value == Decoration.linethrough)
                newText = LINETHROUGH_START + newText + LINETHROUGH_END;
            if (!newText.equals(originalText)) 
                textHandler.setProperty(object, newText);
        }
    }
} public class WindowStyleSupport extends ComponentStyleSupport {
    /** Maps PseudoclassListeners to their WindowListener adapters. */
    private Map<PseudoclassListener, WindowListener> windowListeners;
   
   
    /**
     * Creates a new <code>WindowStyleSupport</code> for the specified class.
     *
     *@param cls the specific window class to support
     */
    public WindowStyleSupport(Class cls) {
        super(cls);
    }
    
    
    @Override
    public Styleable getStyleableParent(Object object) {
        return null;
    }
    
    
    private synchronized WindowListener getWindowListener(
            final PseudoclassListener listener, 
            final PseudoclassEvent event, 
            final boolean inverse) {
        if (windowListeners == null)
            windowListeners = 
                    new HashMap<PseudoclassListener, WindowListener>();
        WindowListener result = windowListeners.get(listener);
        if (result == null) {
            result = new WindowAdapter() {
                @Override
                public void windowActivated(WindowEvent e) {
                    if (inverse)
                        listener.pseudoclassRemoved(event);
                    else
                        listener.pseudoclassAdded(event);
                }
    
    
                @Override
                public void windowDeactivated(WindowEvent e) {
                    if (inverse)
                        listener.pseudoclassAdded(event);
                    else
                        listener.pseudoclassRemoved(event);
                }
            };
            windowListeners.put(listener, result);
        }
        return result;
    }


    @Override
    public void addPseudoclassListener(DefaultStyleable styleable, 
            String pseudoclass, 
            final PseudoclassListener listener) throws StylesheetException {
        final PseudoclassEvent event = new PseudoclassEvent(styleable, 
                pseudoclass);
        Window window = (Window) styleable.getBaseObject();
        if (pseudoclass.equals("active") || pseudoclass.equals("inactive")) {
            boolean inverse = pseudoclass.equals("inactive");
            if (window.isActive() != inverse)
                listener.pseudoclassAdded(event);
            window.addWindowListener(getWindowListener(listener, event, 
                    inverse));
        }
        else
            super.addPseudoclassListener(styleable, pseudoclass, listener);
    }
    
    
    @Override
    public void removePseudoclassListener(DefaultStyleable styleable, 
            String pseudoclass, 
            final PseudoclassListener listener) throws StylesheetException {
        Window window = (Window) styleable.getBaseObject();
        if (pseudoclass.equals("active") || pseudoclass.equals("inactive")) {
            boolean inverse = pseudoclass.equals("inactive");
            window.removeWindowListener(getWindowListener(listener, null, 
                    inverse));
        }
        else
            super.removePseudoclassListener(styleable, pseudoclass, listener);
    }
}
import java.awt.AWTPermission;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Window;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import javax.swing.AbstractButton;
import javax.swing.JLabel;
import javax.swing.KeyStroke;
import javax.swing.border.Border;
import com.sun.stylesheet.Styleable;
import com.sun.stylesheet.StylesheetException;
import com.sun.stylesheet.styleable.DefaultStyleable;
import com.sun.stylesheet.styleable.StyleSupport;
import com.sun.stylesheet.swing.AbstractButtonStyleSupport;
import com.sun.stylesheet.swing.BorderConverter;
import com.sun.stylesheet.swing.ComponentStyleSupport;
import com.sun.stylesheet.swing.DimensionConverter;
import com.sun.stylesheet.swing.FontConverter;
import com.sun.stylesheet.swing.InsetsConverter;
import com.sun.stylesheet.swing.JLabelStyleSupport;
import com.sun.stylesheet.swing.KeyStrokeConverter;
import com.sun.stylesheet.swing.TextDecorationConverter;
import com.sun.stylesheet.swing.TextDecorationHandler;
import com.sun.stylesheet.swing.WindowStyleSupport;

/*
 * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/* Modified by Volker Härtel, 8 Dec 2011 */ package com.sun.stylesheet.types;




/**
 * Returns converters and wrappers for the various types supported by the CSS
 * engine. Additional types can be registered with
 * {@link #registerTypeConverter registerTypeConverter},
 * {@link #registerStyleableWrapper registerStyleableWrapper}, and
 * {@link #registerStyleSupport registerStyleSupport}.
 * 
 * @author Ethan Nicholas
 */
public class TypeManager {
	private static boolean initialized;

	// stores styleable wrappers so we can guarantee that the same wrapper is
	// always used for the same object
	public static Map<Object, WeakReference<Styleable>> styleables = new WeakHashMap<Object, WeakReference<Styleable>>();

	// *** IMPORTANT ***
	// Note that the converters and styleSupport maps are ClassMaps, which means
	// they automatically handle subclasses of the keys inserted into them.
	private static ClassMap<Object> converters = new ClassMap<Object>();

	private static Map<Class, TypeConverter> cachedConverters = new HashMap<Class, TypeConverter>();

	private static ClassMap<Interpolator> interpolators = new ClassMap<Interpolator>();

	private static ClassMap<Class<? extends Styleable>> wrappers = new ClassMap<Class<? extends Styleable>>();

	private static ClassMap<Class<? extends StyleSupport>> styleSupportClasses = new ClassMap<Class<? extends StyleSupport>>();

	private static Map<Class, StyleSupport> styleSupportInstances = new HashMap<Class, StyleSupport>();

	private static Map<Class, Constructor> objectConstructors = new HashMap<Class, Constructor>();

	private static Map<Class, Constructor> classConstructors = new HashMap<Class, Constructor>();

	private TypeManager() { /* not instantiable */
	}

	static {
		registerPrimitiveConverter(boolean.class);
		registerPrimitiveConverter(Boolean.class);
		registerPrimitiveConverter(byte.class);
		registerPrimitiveConverter(Byte.class);
		registerPrimitiveConverter(short.class);
		registerPrimitiveConverter(Short.class);
		registerPrimitiveConverter(int.class);
		registerPrimitiveConverter(Integer.class);
		registerPrimitiveConverter(long.class);
		registerPrimitiveConverter(Long.class);
		registerPrimitiveConverter(float.class);
		registerPrimitiveConverter(Float.class);
		registerPrimitiveConverter(double.class);
		registerPrimitiveConverter(Double.class);
		registerPrimitiveConverter(char.class);
		registerPrimitiveConverter(Character.class);
		registerPrimitiveConverter(String.class);

		registerTypeConverter(String.class, new StringConverter());
		registerTypeConverter(Size.class, new SizeConverter());

		try {
			registerTypeConverter(Time.class, new TimeConverter());
		} catch (NoClassDefFoundError e) {
		}

		registerTypeConverterClass(Enum.class, EnumConverter.class);

		try {
			registerTypeConverter(java.awt.Color.class, new com.sun.stylesheet.swing.ColorConverter());
			registerTypeConverter(Insets.class, new InsetsConverter());
			registerTypeConverter(Dimension.class, new DimensionConverter());
			registerTypeConverter(KeyStroke.class, new KeyStrokeConverter());
			registerTypeConverter(Border.class, new BorderConverter());
			registerTypeConverter(Font.class, new FontConverter());

			registerTypeConverter(TextDecorationHandler.Decoration.class, new TextDecorationConverter());
