/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* CSSBuilder.java
* Creation date: Oct 13, 2005.
* By: Joseph Wong
*/
package org.openquark.cal.caldoc;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTML;
/**
* This class implements a rudimentary builder for constructing Cascading Style Sheets (CSS).
* It is based on a string builder, and does not build up a DOM for the entire document. Rule sets
* however are represented by a simple structured intermediate representation before being appended
* into the buffer.
*
* @author Joseph Wong
*/
final class CSSBuilder {
/** The string builder holding the CSS being built. */
private final StringBuilder buffer = new StringBuilder();
/**
* Represents the notion of a <i>selector</i> in CSS.
*
* @author Joseph Wong
*/
final static class Selector {
/** The CSS representation of the selector. */
private final String cssRep;
/**
* Private constructor.
* Factory methods should be used instead for constructing instances.
*
* @param cssRep the CSS representation of the selector.
*/
private Selector(String cssRep) {
this.cssRep = cssRep;
}
/**
* Creates a generic selector based on the CSS representation.
* @param cssRep the CSS representation of the selector.
* @return the corresponding selector.
*/
static Selector make(String cssRep) {
return new Selector(cssRep);
}
/**
* Creates a type selector based on an HTML tag.
* @param tag the HTML tag.
* @return the corresponding type selector.
*/
static Selector makeType(HTML.Tag tag) {
return new Selector(tag.toString());
}
/**
* Creates a type selector based on an ID.
* @param id the ID.
* @return the corresponding ID selector.
*/
static Selector makeID(String id) {
return new Selector("#" + id);
}
/**
* Creates a class selector based on an HTML style class.
* @param styleClass the HTML style class.
* @return the corresponding class selector.
*/
static Selector makeClass(StyleClass styleClass) {
return new Selector(styleClass.toCSS());
}
/**
* Creates a class selector based on a selector and an HTML style class.
* @param selector the base selector.
* @param styleClass the HTML style class.
* @return the corresponding class selector.
*/
static Selector makeClass(Selector selector, StyleClass styleClass) {
return new Selector(selector.cssRep + styleClass.toCSS());
}
/**
* Creates a pseudo-class selector.
* @param selector the base selector.
* @param pseudoClass the name of the pseudo-class.
* @return the corresponding pseudo-class selector.
*/
static Selector makePseudoClass(Selector selector, String pseudoClass) {
return new Selector(selector.cssRep + ":" + pseudoClass);
}
/**
* Creates a descendant selector with two selectors.
* @param outer the outer selector.
* @param inner the inner selector.
* @return the corresponding descendent selector.
*/
static Selector makeDescendant(Selector outer, Selector inner) {
return new Selector(outer.cssRep + " " + inner.cssRep);
}
/**
* Creates a descendant selector with three selectors.
* @param outer the outer selector.
* @param middle the middle selector.
* @param inner the inner selector.
* @return the corresponding descendent selector.
*/
static Selector makeDescendant(Selector outer, Selector middle, Selector inner) {
return new Selector(outer.cssRep + " " + middle.cssRep + " " + inner.cssRep);
}
/** @return the CSS representation of the selector. */
String toCSS() {
return cssRep;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return cssRep;
}
}
/**
* Represents the notion of a <i>rule set</i> in CSS.
*
* @author Joseph Wong
*/
final static class RuleSet {
/** The number of spaces to use for indentation. */
private static final String INDENT_SPACES = " ";
/** The list of selectors associated with this rule set. */
private final List<Selector> selectors = new ArrayList<Selector>();
/** The insertion-ordered map of attributes to values for this rule set. */
private final LinkedHashMap<String, String> attrValues = new LinkedHashMap<String, String>();
/**
* Constructs a rule set based on one selector.
* @param selector the selector.
*/
RuleSet(Selector selector) {
if (selector == null) {
throw new NullPointerException();
}
selectors.add(selector);
}
/**
* Constructs a rule set based on two selectors.
* @param selector1 the first selector.
* @param selector2 the second selector.
*/
RuleSet(Selector selector1, Selector selector2) {
if (selector1 == null || selector2 == null) {
throw new NullPointerException();
}
selectors.add(selector1);
selectors.add(selector2);
}
/**
* Constructs a rule set based on three selectors.
* @param selector1 the first selector.
* @param selector2 the second selector.
* @param selector3 the third selector.
*/
RuleSet(Selector selector1, Selector selector2, Selector selector3) {
if (selector1 == null || selector2 == null || selector3 == null) {
throw new NullPointerException();
}
selectors.add(selector1);
selectors.add(selector2);
selectors.add(selector3);
}
/**
* Constructs a rule set based on a non-empty array of selectors.
* @param selectorArray a non-empty array of selectors.
*/
RuleSet(Selector[] selectorArray) {
if (selectorArray == null) {
throw new NullPointerException();
}
if (selectorArray.length == 0) {
throw new IllegalArgumentException();
}
for (final Selector selector : selectorArray) {
if (selector == null) {
throw new NullPointerException();
}
selectors.add(selector);
}
}
/**
* Adds an attribute-value pair to this rule set.
* @param attribute the attribute.
* @param value the associated value.
*/
RuleSet addAttribute(String attribute, String value) {
if (attribute == null || value == null) {
throw new NullPointerException();
}
attrValues.put(attribute, value);
return this;
}
/**
* Adds an attribute-value pair to this rule set.
* @param attribute the attribute.
* @param value the associated value.
*/
RuleSet addAttribute(CSS.Attribute attribute, String value) {
if (attribute == null || value == null) {
throw new NullPointerException();
}
addAttribute(attribute.toString(), value);
return this;
}
/** @return the string representation of this rule set. */
String toCSS() {
StringBuilder buffer = new StringBuilder();
for (int i = 0, n = selectors.size(); i < n; i++) {
if (i > 0) {
buffer.append(", ");
}
buffer.append(selectors.get(i).toCSS());
}
buffer.append(" {\n");
for (final Map.Entry<String, String> entry : attrValues.entrySet()) {
buffer.append(INDENT_SPACES).append(entry.getKey()).append(": ").append(entry.getValue()).append(";\n");
}
buffer.append("}\n");
return buffer.toString();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return toCSS();
}
}
/** @return the string representation of the CSS. */
String toCSS() {
return buffer.toString();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return toCSS();
}
/**
* Appends a rule set to the end of the buffer.
* @param ruleSet the rule set.
* @return this CSSBuilder instance.
*/
CSSBuilder addRuleSet(RuleSet ruleSet) {
buffer.append(ruleSet.toCSS()).append('\n');
return this;
}
}