View Javadoc
1   /*
2    * This file is a part of the SchemaSpy project (http://schemaspy.sourceforge.net).
3    * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 John Currier
4    *
5    * SchemaSpy is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *
10   * SchemaSpy is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  package net.sourceforge.schemaspy.view;
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.Reader;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.NoSuchElementException;
33  import java.util.StringTokenizer;
34  import net.sourceforge.schemaspy.Config;
35  import net.sourceforge.schemaspy.model.InvalidConfigurationException;
36  import net.sourceforge.schemaspy.util.LineWriter;
37  
38  /**
39   * Represents our CSS style sheet (CSS) with accessors for important
40   * data from that style sheet.
41   * The idea is that the CSS that will be used to render the HTML pages
42   * also determines the colors used in the generated ER diagrams.
43   *
44   * @author John Currier
45   */
46  public class StyleSheet {
47      private static StyleSheet instance;
48      private final String css;
49      private String bodyBackgroundColor;
50      private String tableHeadBackgroundColor;
51      private String tableBackgroundColor;
52      private String linkColor;
53      private String linkVisitedColor;
54      private String primaryKeyBackgroundColor;
55      private String indexedColumnBackgroundColor;
56      private String selectedTableBackgroundColor;
57      private String excludedColumnBackgroundColor;
58      private final List<String> ids = new ArrayList<String>();
59  
60      private StyleSheet(BufferedReader cssReader) throws IOException {
61          String lineSeparator = System.getProperty("line.separator");
62          StringBuilder data = new StringBuilder();
63          String line;
64  
65          while ((line = cssReader.readLine()) != null) {
66              data.append(line);
67              data.append(lineSeparator);
68          }
69  
70          css = data.toString();
71  
72          int startComment = data.indexOf("/*");
73          while (startComment != -1) {
74              int endComment = data.indexOf("*/");
75              data.replace(startComment, endComment + 2, "");
76              startComment = data.indexOf("/*");
77          }
78  
79          StringTokenizer tokenizer = new StringTokenizer(data.toString(), "{}");
80          String id = null;
81          while (tokenizer.hasMoreTokens()) {
82              String token = tokenizer.nextToken().trim();
83              if (id == null) {
84                  id = token.toLowerCase();
85                  ids.add(id);
86              } else {
87                  Map<String, String> attribs = parseAttributes(token);
88                  if (id.equals(".content"))
89                      bodyBackgroundColor = attribs.get("background");
90                  else if (id.equals("th"))
91                      tableHeadBackgroundColor = attribs.get("background-color");
92                  else if (id.equals("td"))
93                      tableBackgroundColor = attribs.get("background-color");
94                  else if (id.equals(".primarykey"))
95                      primaryKeyBackgroundColor = attribs.get("background");
96                  else if (id.equals(".indexedcolumn"))
97                      indexedColumnBackgroundColor = attribs.get("background");
98                  else if (id.equals(".selectedtable"))
99                      selectedTableBackgroundColor = attribs.get("background");
100                 else if (id.equals(".excludedcolumn"))
101                     excludedColumnBackgroundColor = attribs.get("background");
102                 else if (id.equals("a:link"))
103                     linkColor = attribs.get("color");
104                 else if (id.equals("a:visited"))
105                     linkVisitedColor = attribs.get("color");
106                 id = null;
107             }
108         }
109     }
110 
111     /**
112      * Singleton accessor
113      *
114      * @return the singleton
115      * @throws ParseException
116      */
117     public static StyleSheet getInstance() throws ParseException {
118         if (instance == null) {
119             try {
120                 instance = new StyleSheet(new BufferedReader(getReader(Config.getInstance().getCss())));
121             } catch (IOException exc) {
122                 throw new ParseException(exc);
123             }
124         }
125 
126         return instance;
127     }
128 
129     /**
130      * Returns a {@link Reader} that can be used to read the contents
131      * of the specified css.<p>
132      * Search order is
133      * <ol>
134      * <li><code>cssName</code> as an explicitly-defined file</li>
135      * <li><code>cssName</code> as a file in the user's home directory</li>
136      * <li><code>cssName</code> as a resource from the class path</li>
137      * </ol>
138      *
139      * @param cssName
140      * @return
141      * @throws IOException
142      */
143     private static Reader getReader(String cssName) throws IOException {
144         File cssFile = new File(cssName);
145         if (cssFile.exists())
146             return new FileReader(cssFile);
147         cssFile = new File(System.getProperty("user.dir"), cssName);
148         if (cssFile.exists())
149             return new FileReader(cssFile);
150 
151         InputStream cssStream = StyleSheet.class.getClassLoader().getResourceAsStream(cssName);
152         if (cssStream == null)
153             throw new ParseException("Unable to find requested style sheet: " + cssName);
154 
155         return new InputStreamReader(cssStream);
156     }
157 
158     private Map<String, String> parseAttributes(String data) {
159         Map<String, String> attribs = new HashMap<String, String>();
160 
161         try {
162             StringTokenizer attrTokenizer = new StringTokenizer(data, ";");
163             while (attrTokenizer.hasMoreTokens()) {
164                 StringTokenizer pairTokenizer = new StringTokenizer(attrTokenizer.nextToken(), ":");
165                 String attribute = pairTokenizer.nextToken().trim().toLowerCase();
166                 String value = pairTokenizer.nextToken().trim().toLowerCase();
167                 attribs.put(attribute, value);
168             }
169         } catch (NoSuchElementException badToken) {
170             System.err.println("Failed to extract attributes from '" + data + "'");
171             throw badToken;
172         }
173 
174         return attribs;
175     }
176 
177     /**
178      * Write the contents of the original css to <code>out</code>.
179      *
180      * @param out
181      * @throws IOException
182      */
183     public void write(LineWriter out) throws IOException {
184         out.write(css);
185     }
186 
187     public String getBodyBackground() {
188         if (bodyBackgroundColor == null)
189             throw new MissingCssPropertyException(".content", "background");
190 
191         return bodyBackgroundColor;
192     }
193 
194     public String getTableBackground() {
195         if (tableBackgroundColor == null)
196             throw new MissingCssPropertyException("td", "background-color");
197 
198         return tableBackgroundColor;
199     }
200 
201     public String getTableHeadBackground() {
202         if (tableHeadBackgroundColor == null)
203             throw new MissingCssPropertyException("th", "background-color");
204 
205         return tableHeadBackgroundColor;
206     }
207 
208     public String getPrimaryKeyBackground() {
209         if (primaryKeyBackgroundColor == null)
210             throw new MissingCssPropertyException(".primaryKey", "background");
211 
212         return primaryKeyBackgroundColor;
213     }
214 
215     public String getIndexedColumnBackground() {
216         if (indexedColumnBackgroundColor == null)
217             throw new MissingCssPropertyException(".indexedColumn", "background");
218 
219         return indexedColumnBackgroundColor;
220     }
221 
222     public String getSelectedTableBackground() {
223         if (selectedTableBackgroundColor == null)
224             throw new MissingCssPropertyException(".selectedTable", "background");
225 
226         return selectedTableBackgroundColor;
227     }
228 
229     public String getExcludedColumnBackgroundColor() {
230         if (excludedColumnBackgroundColor == null)
231             throw new MissingCssPropertyException(".excludedColumn", "background");
232 
233         return excludedColumnBackgroundColor;
234     }
235 
236     public String getLinkColor() {
237         if (linkColor == null)
238             throw new MissingCssPropertyException("a:link", "color");
239 
240         return linkColor;
241     }
242 
243     public String getLinkVisitedColor() {
244         if (linkVisitedColor == null)
245             throw new MissingCssPropertyException("a:visited", "color");
246 
247         return linkVisitedColor;
248     }
249 
250     /**
251      * Indicates that a css property was missing
252      */
253     public static class MissingCssPropertyException extends InvalidConfigurationException {
254         private static final long serialVersionUID = 1L;
255 
256         /**
257          * @param cssSection name of the css section
258          * @param propName name of the missing property in that section
259          */
260         public MissingCssPropertyException(String cssSection, String propName) {
261             super("Required property '" + propName + "' was not found for the definition of '" + cssSection + "' in " + Config.getInstance().getCss());
262         }
263     }
264 
265     /**
266      * Indicates an exception in parsing the css
267      */
268     public static class ParseException extends InvalidConfigurationException {
269         private static final long serialVersionUID = 1L;
270 
271         /**
272          * @param cause root exception that caused the failure
273          */
274         public ParseException(Exception cause) {
275             super(cause);
276         }
277 
278         /**
279          * @param msg textual description of the failure
280          */
281         public ParseException(String msg) {
282             super(msg);
283         }
284     }
285 }