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 }