001    /*--------------------------------------------------------------------------+
002    $Id: CSSManagerBase.java 26283 2010-02-18 11:18:57Z juergens $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.html;
019    
020    import java.io.PrintStream;
021    import java.util.IdentityHashMap;
022    import java.util.Map;
023    
024    import edu.tum.cs.commons.collections.TwoDimHashMap;
025    
026    /**
027     * This class is used for managing cascading style sheets. It keeps track of all
028     * declaration blocks and selectors used.
029     * 
030     * @author hummelb
031     * @author $Author: juergens $
032     * @version $Rev: 26283 $
033     * @levd.rating GREEN Hash: D0645454B949512DFB3949CF2F8C7055
034     */
035    public abstract class CSSManagerBase {
036    
037            /** Default declarations for elements (i.e. without class). */
038            private final TwoDimHashMap<EHTMLElement, ECSSPseudoClass, CSSDeclarationBlock> defaultDeclarations = new TwoDimHashMap<EHTMLElement, ECSSPseudoClass, CSSDeclarationBlock>();
039    
040            /** The names the declaration blocks are registered with. */
041            private final Map<CSSDeclarationBlock, String> classNames = new IdentityHashMap<CSSDeclarationBlock, String>();
042    
043            /** Counter used for generating unique CSS class names. */
044            private static int classCounter = 0;
045    
046            /** Returns whether there is a default declaration for the given element. */
047            public boolean hasDefaultDeclaration(EHTMLElement element) {
048                    return hasDefaultDeclaration(element, ECSSPseudoClass.NONE);
049            }
050    
051            /** Returns whether there is a default declaration for the given element. */
052            public boolean hasDefaultDeclaration(EHTMLElement element,
053                            ECSSPseudoClass pseudoClass) {
054                    return defaultDeclarations.containsKey(element, pseudoClass);
055            }
056    
057            /** Adds a single selector and its block to this manager. */
058            public final void addDefaultDeclaration(EHTMLElement element,
059                            CSSDeclarationBlock block) {
060                    addDefaultDeclaration(element, ECSSPseudoClass.NONE, block);
061            }
062    
063            /** Adds a single selector and its block to this manager. */
064            public void addDefaultDeclaration(EHTMLElement element,
065                            ECSSPseudoClass pseudoClass, CSSDeclarationBlock block) {
066                    if (defaultDeclarations.containsKey(element, pseudoClass)) {
067                            throw new IllegalStateException("May not add element " + element
068                                            + " twice.");
069                    }
070                    if (!element.allowsAttribute(EHTMLAttribute.STYLE)) {
071                            throw new IllegalArgumentException("The given element "
072                                            + element.getName() + " does not support styles!");
073                    }
074                    defaultDeclarations.putValue(element, pseudoClass, block);
075            }
076    
077            /**
078             * Returns the name of the CSS class used for this block. If the block is
079             * not yet known, it is registered with this manager.
080             */
081            public String getCSSClassName(CSSDeclarationBlock block) {
082                    String name = classNames.get(block);
083                    if (name == null) {
084                            name = generateCSSClassName();
085                            classNames.put(block, name);
086                    }
087                    return name;
088            }
089    
090            /**
091             * Generates a suitable name for a CSS class. This may be overridden by
092             * subclasses. However it must be made sure, that the class names returned
093             * are unique and do not overlap with HTML element names.
094             */
095            protected String generateCSSClassName() {
096                    return "CSSCLASS" + ++classCounter;
097            }
098    
099            /**
100             * Write all selectors with their blocks to the given stream. The format is
101             * the one usually used in CSS files. This merely calls
102             * {@link #writeOutDefaultDeclarations(PrintStream)} and
103             * {@link #writeOutDeclarations(PrintStream)}.
104             */
105            protected void writeOut(PrintStream ps) {
106    
107                    writeOutDefaultDeclarations(ps);
108                    writeOutDeclarations(ps);
109            }
110    
111            /** Write out default declarations for element (i.e. without specific class). */
112            protected void writeOutDefaultDeclarations(PrintStream ps) {
113                    for (EHTMLElement element : defaultDeclarations.getFirstKeys()) {
114                            for (ECSSPseudoClass pseudocssClass : defaultDeclarations
115                                            .getSecondKeys(element)) {
116                                    String selector = element.getName() + pseudocssClass.getName();
117                                    writeBlock(defaultDeclarations
118                                                    .getValue(element, pseudocssClass), selector, ps);
119                            }
120                    }
121            }
122    
123            /** Write out declarations. */
124            protected void writeOutDeclarations(PrintStream ps) {
125                    for (Map.Entry<CSSDeclarationBlock, String> entry : classNames
126                                    .entrySet()) {
127                            writeBlock(entry.getKey(), "." + entry.getValue(), ps);
128                    }
129            }
130    
131            /** Writes a single block/selector pair to the given stream. */
132            private void writeBlock(CSSDeclarationBlock block, String selector,
133                            PrintStream ps) {
134                    ps.print(selector);
135                    ps.println(" {");
136                    block.writeOut(ps, "  ");
137                    ps.println("}");
138                    ps.println();
139            }
140    }