0001: /**
0002: * ===========================================
0003: * JFreeReport : a free Java reporting library
0004: * ===========================================
0005: *
0006: * Project Info: http://reporting.pentaho.org/
0007: *
0008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
0009: *
0010: * This library is free software; you can redistribute it and/or modify it under the terms
0011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
0012: * either version 2.1 of the License, or (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
0015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0016: * See the GNU Lesser General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU Lesser General Public License along with this
0019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
0020: * Boston, MA 02111-1307, USA.
0021: *
0022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0023: * in the United States and other countries.]
0024: *
0025: * ------------
0026: * ElementStyleSheet.java
0027: * ------------
0028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
0029: */package org.jfree.report.style;
0030:
0031: import java.awt.geom.Dimension2D;
0032: import java.awt.geom.Point2D;
0033: import java.io.IOException;
0034: import java.io.ObjectInputStream;
0035: import java.io.ObjectOutputStream;
0036: import java.io.Serializable;
0037: import java.util.ArrayList;
0038: import java.util.Collections;
0039: import java.util.Iterator;
0040:
0041: import org.jfree.serializer.SerializerHelper;
0042: import org.jfree.ui.FloatDimension;
0043: import org.jfree.util.ObjectUtilities;
0044:
0045: /**
0046: * An element style-sheet contains zero, one or many attributes that affect the appearance of report elements. For each
0047: * attribute, there is a predefined key that can be used to access that attribute in the style sheet.
0048: * <p/>
0049: * Every report element has an associated style-sheet.
0050: * <p/>
0051: * A style-sheet maintains a list of parent style-sheets. If an attribute is not defined in a style-sheet, the code
0052: * refers to the parent style-sheets to see if the attribute is defined there.
0053: * <p/>
0054: * All StyleSheet entries are checked against the StyleKeyDefinition for validity.
0055: * <p/>
0056: * As usual, this implementation is not synchronized, we need the performance during the reporting.
0057: *
0058: * @author Thomas Morgner
0059: */
0060: public abstract class ElementStyleSheet extends AbstractStyleSheet
0061: implements Serializable, StyleChangeListener, Cloneable {
0062: /**
0063: * A key for the 'minimum size' of an element. This style property is not inherited from the parent band.
0064: *
0065: * @deprecated use the minimum-width and minimum-height style-keys instead.
0066: */
0067: public static final StyleKey MINIMUMSIZE = ElementStyleKeys.MINIMUMSIZE;
0068: /**
0069: * A key for the 'maximum size' of an element. This style property is not inherited from the parent band.
0070: *
0071: * @deprecated use the minimum-width and minimum-height style-keys instead.
0072: */
0073: public static final StyleKey MAXIMUMSIZE = ElementStyleKeys.MAXIMUMSIZE;
0074:
0075: /**
0076: * A key for the 'preferred size' of an element. This style property is not inherited from the parent band.
0077: *
0078: * @deprecated use the minimum-width and minimum-height style-keys instead.
0079: */
0080: public static final StyleKey PREFERREDSIZE = ElementStyleKeys.PREFERREDSIZE;
0081: /**
0082: * A key for the 'bounds' of an element. This style property is not inherited from the parent band. This style
0083: * property is an internal state property and is therefore not written to the XML.
0084: *
0085: * @deprecated no longer used.
0086: */
0087: public static final StyleKey BOUNDS = ElementStyleKeys.BOUNDS;
0088:
0089: /**
0090: * A key for an element's 'visible' flag.
0091: */
0092: public static final StyleKey VISIBLE = ElementStyleKeys.VISIBLE;
0093:
0094: /**
0095: * A key for the 'paint' used to color an element. For historical reasons, this key requires a color value.
0096: */
0097: public static final StyleKey PAINT = ElementStyleKeys.PAINT;
0098:
0099: /**
0100: * A key for the 'ext-paint' used to fill or draw an element. If the specified paint is not supported by the output
0101: * target, the color given with the 'paint' key is used instead.
0102: */
0103: public static final StyleKey EXTPAINT = ElementStyleKeys.EXTPAINT;
0104:
0105: /**
0106: * A key for the 'stroke' used to draw an element. (This now only applies to shape and drawable-elements.)
0107: */
0108: public static final StyleKey STROKE = ElementStyleKeys.STROKE;
0109:
0110: /**
0111: * A key for the horizontal alignment of an element.
0112: */
0113: public static final StyleKey ALIGNMENT = ElementStyleKeys.ALIGNMENT;
0114:
0115: /**
0116: * A key for the vertical alignment of an element.
0117: */
0118: public static final StyleKey VALIGNMENT = ElementStyleKeys.VALIGNMENT;
0119:
0120: /**
0121: * A key for an element's 'scale' flag.
0122: */
0123: public static final StyleKey SCALE = ElementStyleKeys.SCALE;
0124:
0125: /**
0126: * A key for an element's 'keep aspect ratio' flag.
0127: */
0128: public static final StyleKey KEEP_ASPECT_RATIO = ElementStyleKeys.KEEP_ASPECT_RATIO;
0129:
0130: /**
0131: * A key for the dynamic height flag for an element.
0132: */
0133: public static final StyleKey DYNAMIC_HEIGHT = ElementStyleKeys.DYNAMIC_HEIGHT;
0134:
0135: /**
0136: * The Layout Cacheable stylekey. Set this stylekey to false, to define that the element is not cachable. This key
0137: * defaults to true.
0138: *
0139: * @deprecated This property is no longer used.
0140: */
0141: public static final StyleKey ELEMENT_LAYOUT_CACHEABLE = ElementStyleKeys.ELEMENT_LAYOUT_CACHEABLE;
0142:
0143: public static final StyleKey HREF_TARGET = ElementStyleKeys.HREF_TARGET;
0144:
0145: /**
0146: * Specifies the anchor tag's target window for opening the link.
0147: */
0148: public static final StyleKey HREF_WINDOW = ElementStyleKeys.HREF_WINDOW;
0149:
0150: /**
0151: * An internal flag style indicating whether the current HRef is inherited from a child.
0152: * <p/>
0153: * This style property is an internal state property and is therefore not written to the XML.
0154: */
0155: public static final StyleKey HREF_INHERITED = ElementStyleKeys.HREF_INHERITED;
0156:
0157: /**
0158: * The StyleKey for the user defined cell data format.
0159: */
0160: public static final StyleKey EXCEL_WRAP_TEXT = ElementStyleKeys.EXCEL_WRAP_TEXT;
0161:
0162: /**
0163: * The StyleKey for the user defined cell data format.
0164: */
0165: public static final StyleKey EXCEL_DATA_FORMAT_STRING = ElementStyleKeys.EXCEL_DATA_FORMAT_STRING;
0166:
0167: /**
0168: * A key for the 'font family' used to draw element text.
0169: */
0170: public static final StyleKey FONT = TextStyleKeys.FONT;
0171:
0172: /**
0173: * A key for the 'font size' used to draw element text.
0174: */
0175: public static final StyleKey FONTSIZE = TextStyleKeys.FONTSIZE;
0176:
0177: /**
0178: * A key for the 'font size' used to draw element text.
0179: */
0180: public static final StyleKey LINEHEIGHT = TextStyleKeys.LINEHEIGHT;
0181:
0182: /**
0183: * A key for an element's 'bold' flag.
0184: */
0185: public static final StyleKey BOLD = TextStyleKeys.BOLD;
0186:
0187: /**
0188: * A key for an element's 'italic' flag.
0189: */
0190: public static final StyleKey ITALIC = TextStyleKeys.ITALIC;
0191:
0192: /**
0193: * A key for an element's 'underlined' flag.
0194: */
0195: public static final StyleKey UNDERLINED = TextStyleKeys.UNDERLINED;
0196:
0197: /**
0198: * A key for an element's 'strikethrough' flag.
0199: */
0200: public static final StyleKey STRIKETHROUGH = TextStyleKeys.STRIKETHROUGH;
0201:
0202: /**
0203: * A key for an element's 'embedd' flag.
0204: */
0205: public static final StyleKey EMBEDDED_FONT = TextStyleKeys.EMBEDDED_FONT;
0206:
0207: /**
0208: * A key for an element's 'embedd' flag.
0209: */
0210: public static final StyleKey FONTENCODING = TextStyleKeys.FONTENCODING;
0211:
0212: /**
0213: * The string that is used to end a text if not all text fits into the element.
0214: */
0215: public static final StyleKey RESERVED_LITERAL = TextStyleKeys.RESERVED_LITERAL;
0216:
0217: /**
0218: * The Layout Cacheable stylekey. Set this stylekey to false, to define that the element is not cachable. This key
0219: * defaults to true.
0220: */
0221: public static final StyleKey TRIM_TEXT_CONTENT = TextStyleKeys.TRIM_TEXT_CONTENT;
0222:
0223: /**
0224: * A singleton marker for the cache.
0225: */
0226: private static final Object UNDEFINED_VALUE = new Object();
0227:
0228: /**
0229: * The style-sheet name.
0230: */
0231: private String name;
0232:
0233: /**
0234: * Storage for the parent style sheets (if any).
0235: */
0236: private ArrayList parents;
0237:
0238: /**
0239: * Storage for readonly style sheets.
0240: */
0241: private ElementDefaultStyleSheet globalDefaultStyleSheet;
0242: private ElementStyleSheet cascadeStyleSheet;
0243:
0244: /**
0245: * Parent style sheet cache.
0246: */
0247: private transient StyleSheetCarrier[] parentsCached;
0248:
0249: private transient StyleKey[] propertyKeys;
0250: private transient Object[] properties;
0251: private transient Object[] cachedProperties;
0252: private transient Object[] cachedData;
0253:
0254: /**
0255: * Style change support.
0256: */
0257: private transient StyleChangeSupport styleChangeSupport;
0258:
0259: /**
0260: * A flag that controls whether or not caching is allowed.
0261: */
0262: private boolean allowCaching;
0263: /**
0264: * The cached font definition instance from this stylessheet.
0265: */
0266: private transient FontDefinition fontDefinition;
0267:
0268: private long changeTracker;
0269: private static final StyleKey[] EMPTY_KEYS = new StyleKey[0];
0270:
0271: /**
0272: * Creates a new element style-sheet with the given name. The style-sheet initially contains no attributes, and has
0273: * no parent style-sheets.
0274: *
0275: * @param name the name (<code>null</code> not permitted).
0276: */
0277: protected ElementStyleSheet(final String name) {
0278: if (name == null) {
0279: throw new NullPointerException(
0280: "ElementStyleSheet constructor: name is null.");
0281: }
0282: this .name = name;
0283: this .parents = new ArrayList(5);
0284: this .styleChangeSupport = new StyleChangeSupport(this );
0285: }
0286:
0287: /**
0288: * Creates a new element style-sheet with the given name. The style-sheet initially contains no attributes, and has
0289: * no parent style-sheets.
0290: *
0291: * @param name the name (<code>null</code> not permitted).
0292: */
0293: protected ElementStyleSheet(final String name,
0294: final ElementStyleSheet deriveParent) {
0295: if (name == null) {
0296: throw new NullPointerException(
0297: "ElementStyleSheet constructor: name is null.");
0298: }
0299: if (deriveParent == null) {
0300: throw new NullPointerException(
0301: "ElementStyleSheet constructor: parent cannot be null.");
0302: }
0303: this .name = name;
0304: this .parents = new ArrayList(5);
0305: this .styleChangeSupport = new StyleChangeSupport(this );
0306: // These arrays are immutable ..
0307: this .propertyKeys = deriveParent.propertyKeys;
0308: this .properties = new Object[propertyKeys.length];
0309: // Cached properties are not always needed ..
0310: this .changeTracker = deriveParent.changeTracker;
0311: }
0312:
0313: /**
0314: * Returns <code>true</code> if caching is allowed, and <code>false</code> otherwise.
0315: *
0316: * @return A boolean.
0317: */
0318: public final boolean isAllowCaching() {
0319: return allowCaching;
0320: }
0321:
0322: public long getChangeTracker() {
0323: return changeTracker;
0324: }
0325:
0326: /**
0327: * Returns true, if the given key is locally defined, false otherwise.
0328: *
0329: * @param key the key to test
0330: * @return true, if the key is local, false otherwise.
0331: */
0332: public boolean isLocalKey(final StyleKey key) {
0333: if (properties == null) {
0334: return false;
0335: }
0336: final int identifier = key.getIdentifier();
0337: if (properties.length <= identifier) {
0338: return false;
0339: }
0340: return properties[identifier] != null;
0341: }
0342:
0343: /**
0344: * Sets the flag that controls whether or not caching is allowed.
0345: *
0346: * @param allowCaching the flag value.
0347: */
0348: public void setAllowCaching(final boolean allowCaching) {
0349: this .allowCaching = allowCaching;
0350: if (this .allowCaching == false) {
0351: this .cachedProperties = null;
0352: }
0353: }
0354:
0355: /**
0356: * Returns the name of the style-sheet.
0357: *
0358: * @return the name (never <code>null</code>).
0359: */
0360: public String getName() {
0361: return name;
0362: }
0363:
0364: /**
0365: * Adds a parent style-sheet. This method adds the parent to the beginning of the list, and guarantees, that this
0366: * parent is queried first.
0367: *
0368: * @param parent the parent (<code>null</code> not permitted).
0369: */
0370: public void addParent(final ElementStyleSheet parent) {
0371: addParent(0, parent);
0372: }
0373:
0374: /**
0375: * Adds a parent style-sheet. Parents on a lower position are queried before any parent with an higher position in the
0376: * list.
0377: *
0378: * @param position the position where to insert the parent style sheet
0379: * @param parent the parent (<code>null</code> not permitted).
0380: * @throws IndexOutOfBoundsException if the position is invalid (pos < 0 or pos >= numberOfParents)
0381: */
0382: public void addParent(final int position,
0383: final ElementStyleSheet parent) {
0384: if (parent == null) {
0385: throw new NullPointerException(
0386: "ElementStyleSheet.addParent(...): parent is null.");
0387: }
0388: if (parent.isSubStyleSheet(this ) == false) {
0389: final StyleSheetCarrier carrier = createCarrier(parent);
0390: if (carrier == null) {
0391: throw new IllegalArgumentException(
0392: "The given StyleSheet cannot be added to this stylesheet.");
0393: }
0394: parents.add(position, carrier);
0395: parentsCached = null;
0396: } else {
0397: throw new IllegalArgumentException(
0398: "Cannot add parent as child.");
0399: }
0400: }
0401:
0402: protected abstract StyleSheetCarrier createCarrier(
0403: ElementStyleSheet styleSheet);
0404:
0405: /**
0406: * Checks, whether the given element stylesheet is already added as child into the stylesheet tree.
0407: *
0408: * @param parent the element that should be tested.
0409: * @return true, if the element is a child of this element style sheet, false otherwise.
0410: */
0411: protected boolean isSubStyleSheet(final ElementStyleSheet parent) {
0412: for (int i = 0; i < parents.size(); i++) {
0413: final StyleSheetCarrier ca = (StyleSheetCarrier) parents
0414: .get(i);
0415: final ElementStyleSheet es = ca.getStyleSheet();
0416: if (es == parent) {
0417: return true;
0418: }
0419: if (es.isSubStyleSheet(parent) == true) {
0420: return true;
0421: }
0422: }
0423: return false;
0424: }
0425:
0426: /**
0427: * Removes a parent style-sheet.
0428: *
0429: * @param parent the style-sheet to remove (<code>null</code> not permitted).
0430: */
0431: public void removeParent(final ElementStyleSheet parent) {
0432: if (parent == null) {
0433: throw new NullPointerException(
0434: "ElementStyleSheet.removeParent(...): parent is null.");
0435: }
0436: final Iterator it = parents.iterator();
0437: while (it.hasNext()) {
0438: final StyleSheetCarrier carrier = (StyleSheetCarrier) it
0439: .next();
0440: if (carrier.isSame(parent)) {
0441: it.remove();
0442: carrier.invalidate();
0443: }
0444: }
0445: parentsCached = null;
0446: }
0447:
0448: /**
0449: * Returns a list of the parent style-sheets.
0450: * <p/>
0451: * The list is unmodifiable.
0452: *
0453: * @return the list.
0454: */
0455: public ElementStyleSheet[] getParents() {
0456: if (parentsCached == null) {
0457: this .parentsToCache();
0458: }
0459: final ElementStyleSheet[] styleSheets = new ElementStyleSheet[parentsCached.length];
0460: for (int i = 0; i < styleSheets.length; i++) {
0461: styleSheets[i] = parentsCached[i].getStyleSheet();
0462: }
0463: return styleSheets;
0464: }
0465:
0466: /**
0467: * Returns the global default (if defined).
0468: *
0469: * @return the list.
0470: */
0471: public ElementDefaultStyleSheet getGlobalDefaultStyleSheet() {
0472: return globalDefaultStyleSheet;
0473: }
0474:
0475: public void setGlobalDefaultStyleSheet(
0476: final ElementDefaultStyleSheet defaultStyleSheet) {
0477: this .globalDefaultStyleSheet = defaultStyleSheet;
0478: }
0479:
0480: public ElementStyleSheet getCascadeStyleSheet() {
0481: return cascadeStyleSheet;
0482: }
0483:
0484: public void setCascadeStyleSheet(
0485: final ElementStyleSheet cascadeStyleSheet) {
0486: if (this .cascadeStyleSheet != null) {
0487: this .cascadeStyleSheet.removeListener(this );
0488: }
0489: this .cascadeStyleSheet = cascadeStyleSheet;
0490: if (this .cascadeStyleSheet != null) {
0491: this .cascadeStyleSheet.addListener(this );
0492: }
0493: }
0494:
0495: public final Object[] toArray(final StyleKey[] keys) {
0496: if (cachedData != null) {
0497: return (Object[]) cachedData.clone();
0498: }
0499:
0500: final Object[] data = new Object[keys.length];
0501:
0502: // Step 1: Copy all the properties which are set directly.
0503: if (properties != null) {
0504: final int maxIdx = Math.min(keys.length, properties.length);
0505: System.arraycopy(properties, 0, data, 0, maxIdx);
0506: }
0507:
0508: // Step 2: Copy all cached properties.
0509: if (cachedProperties != null) {
0510: for (int i = 0; i < keys.length; i++) {
0511: final StyleKey key = keys[i];
0512: final int identifier = key.getIdentifier();
0513:
0514: if (identifier >= cachedProperties.length) {
0515: continue;
0516: }
0517: if (data[identifier] != null) {
0518: continue;
0519: }
0520:
0521: final Object value = cachedProperties[identifier];
0522: if (value != null) {
0523: if (value == UNDEFINED_VALUE) {
0524: data[identifier] = null;
0525: } else {
0526: data[identifier] = value;
0527: }
0528: }
0529: }
0530: }
0531:
0532: // Step 3: Copy all remaining properties
0533: for (int i = 0; i < keys.length; i++) {
0534: final StyleKey key = keys[i];
0535: if (key == null) {
0536: continue;
0537: }
0538:
0539: final int identifier = key.getIdentifier();
0540:
0541: if (data[identifier] != null) {
0542: continue;
0543: }
0544: data[identifier] = getStyleProperty(key, null);
0545: }
0546:
0547: if (allowCaching) {
0548: cachedData = data;
0549: return (Object[]) data.clone();
0550: }
0551: return data;
0552: }
0553:
0554: /**
0555: * Returns the value of a style. If the style is not found in this style-sheet, the code looks in the parent
0556: * style-sheets. If the style is not found in any of the parent style-sheets, then the default value (possibly
0557: * <code>null</code>) is returned.
0558: *
0559: * @param key the style key.
0560: * @param defaultValue the default value (<code>null</code> permitted).
0561: * @return the value.
0562: */
0563: public Object getStyleProperty(final StyleKey key,
0564: final Object defaultValue) {
0565: final Object legacyVal = handleGetLegacyKeys(key);
0566: if (legacyVal == UNDEFINED_VALUE) {
0567: return defaultValue;
0568: } else if (legacyVal != null) {
0569: return legacyVal;
0570: }
0571:
0572: final int identifier = key.getIdentifier();
0573: if (properties != null) {
0574: if (properties.length > identifier) {
0575: final Object value = properties[identifier];
0576: if (value != null) {
0577: return value;
0578: }
0579: }
0580: }
0581:
0582: if (cachedProperties != null) {
0583: if (cachedProperties.length > identifier) {
0584: final Object value = cachedProperties[identifier];
0585: if (value != null) {
0586: if (value == UNDEFINED_VALUE) {
0587: return defaultValue;
0588: }
0589: return value;
0590: }
0591: }
0592: }
0593:
0594: // parents must always be queried ...
0595: parentsToCache();
0596: for (int i = 0; i < parentsCached.length; i++) {
0597: final ElementStyleSheet st = parentsCached[i]
0598: .getStyleSheet();
0599: final Object value = st.getStyleProperty(key, null);
0600: if (value == null) {
0601: continue;
0602: }
0603: putInCache(key, value);
0604: return value;
0605: }
0606:
0607: if (key.isInheritable()) {
0608: if (cascadeStyleSheet != null) {
0609: final Object value = cascadeStyleSheet
0610: .getStyleProperty(key, null);
0611: if (value != null) {
0612: putInCache(key, value);
0613: return value;
0614: }
0615: }
0616: }
0617:
0618: if (globalDefaultStyleSheet != null) {
0619: final Object value = globalDefaultStyleSheet
0620: .getStyleProperty(key, null);
0621: if (value != null) {
0622: putInCache(key, value);
0623: return value;
0624: }
0625: }
0626:
0627: putInCache(key, UNDEFINED_VALUE);
0628: return defaultValue;
0629: }
0630:
0631: /**
0632: * Puts an object into the cache (if caching is enabled).
0633: *
0634: * @param key the stylekey for that object
0635: * @param value the object.
0636: */
0637: private void putInCache(final StyleKey key, final Object value) {
0638: if (allowCaching) {
0639: final int identifier = key.getIdentifier();
0640: if (cachedProperties != null) {
0641: if (cachedProperties.length <= identifier) {
0642: final Object[] newCache = new Object[StyleKey
0643: .getDefinedStyleKeyCount()];
0644: System.arraycopy(cachedProperties, 0, newCache, 0,
0645: cachedProperties.length);
0646: cachedProperties = newCache;
0647: }
0648: } else {
0649: cachedProperties = new Object[StyleKey
0650: .getDefinedStyleKeyCount()];
0651: }
0652: cachedProperties[identifier] = value;
0653: cachedData = null;
0654: }
0655: }
0656:
0657: /**
0658: * Sets a boolean style property.
0659: *
0660: * @param key the style key (<code>null</code> not permitted).
0661: * @param value the value.
0662: * @throws NullPointerException if the given key is null.
0663: * @throws ClassCastException if the value cannot be assigned with the given key.
0664: */
0665: public void setBooleanStyleProperty(final StyleKey key,
0666: final boolean value) {
0667: if (value) {
0668: setStyleProperty(key, Boolean.TRUE);
0669: } else {
0670: setStyleProperty(key, Boolean.FALSE);
0671: }
0672: }
0673:
0674: /**
0675: * Sets a style property (or removes the style if the value is <code>null</code>).
0676: *
0677: * @param key the style key (<code>null</code> not permitted).
0678: * @param value the value.
0679: * @throws NullPointerException if the given key is null.
0680: * @throws ClassCastException if the value cannot be assigned with the given key.
0681: */
0682: public void setStyleProperty(final StyleKey key, final Object value) {
0683: if (key == null) {
0684: throw new NullPointerException(
0685: "ElementStyleSheet.setStyleProperty: key is null.");
0686: }
0687: if (isFontDefinitionProperty(key)) {
0688: fontDefinition = null;
0689: }
0690: if (handleSetLegacyKeys(key, value)) {
0691: return;
0692: }
0693:
0694: final int identifier = key.getIdentifier();
0695: if (value == null) {
0696: if (properties != null) {
0697: if (properties.length > identifier) {
0698: if (properties[identifier] == null) {
0699: return;
0700: }
0701:
0702: // invalidate the cache ..
0703: if (allowCaching && cachedData != null) {
0704: putInCache(key, null);
0705: }
0706:
0707: changeTracker += 1;
0708: properties[identifier] = null;
0709: }
0710: }
0711:
0712: if (propertyKeys != null) {
0713: if (propertyKeys.length > identifier) {
0714: propertyKeys[identifier] = null;
0715: }
0716: }
0717: styleChangeSupport.fireStyleRemoved(key);
0718: return;
0719: }
0720:
0721: if (key.getValueType().isAssignableFrom(value.getClass()) == false) {
0722: throw new ClassCastException("Value for key "
0723: + key.getName() + " is not assignable: "
0724: + value.getClass() + " is not assignable from "
0725: + key.getValueType());
0726: }
0727: if (properties != null) {
0728: if (properties.length <= identifier) {
0729: final Object[] newProps = new Object[StyleKey
0730: .getDefinedStyleKeyCount()];
0731: System.arraycopy(properties, 0, newProps, 0,
0732: properties.length);
0733: properties = newProps;
0734: }
0735: } else {
0736: properties = new Object[StyleKey.getDefinedStyleKeyCount()];
0737: }
0738:
0739: if (propertyKeys != null) {
0740: if (propertyKeys.length <= identifier) {
0741: final StyleKey[] newProps = new StyleKey[StyleKey
0742: .getDefinedStyleKeyCount()];
0743: System.arraycopy(propertyKeys, 0, newProps, 0,
0744: propertyKeys.length);
0745: propertyKeys = newProps;
0746: }
0747: } else {
0748: propertyKeys = new StyleKey[StyleKey
0749: .getDefinedStyleKeyCount()];
0750: }
0751: if (ObjectUtilities.equal(properties[identifier], value)) {
0752: // no need th change anything ..
0753: return;
0754: }
0755:
0756: // invalidate the cache ..
0757: if (allowCaching && cachedData != null) {
0758: putInCache(key, value);
0759: }
0760:
0761: changeTracker += 1;
0762: properties[identifier] = value;
0763: propertyKeys[identifier] = key;
0764:
0765: styleChangeSupport.fireStyleChanged(key, value);
0766: }
0767:
0768: private boolean handleSetLegacyKeys(final StyleKey key,
0769: final Object value) {
0770: if (value == null) {
0771: return false;
0772: }
0773: if (key == ElementStyleKeys.ABSOLUTE_POS) {
0774: final Point2D point = (Point2D) value;
0775: setStyleProperty(ElementStyleKeys.POS_X, new Float(point
0776: .getX()));
0777: setStyleProperty(ElementStyleKeys.POS_Y, new Float(point
0778: .getY()));
0779: return true;
0780: }
0781: if (key == ElementStyleKeys.MINIMUMSIZE) {
0782: final Dimension2D dim = (Dimension2D) value;
0783: setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(dim
0784: .getWidth()));
0785: setStyleProperty(ElementStyleKeys.MIN_HEIGHT, new Float(dim
0786: .getHeight()));
0787: return true;
0788: }
0789: if (key == ElementStyleKeys.MAXIMUMSIZE) {
0790: final Dimension2D dim = (Dimension2D) value;
0791: setStyleProperty(ElementStyleKeys.MAX_WIDTH, new Float(dim
0792: .getWidth()));
0793: setStyleProperty(ElementStyleKeys.MAX_HEIGHT, new Float(dim
0794: .getHeight()));
0795: return true;
0796: }
0797: if (key == ElementStyleKeys.PREFERREDSIZE) {
0798: final Dimension2D dim = (Dimension2D) value;
0799: setStyleProperty(ElementStyleKeys.WIDTH, new Float(dim
0800: .getWidth()));
0801: setStyleProperty(ElementStyleKeys.HEIGHT, new Float(dim
0802: .getHeight()));
0803: return true;
0804: }
0805: if (key == ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS) {
0806: final Dimension2D dim = (Dimension2D) value;
0807: setStyleProperty(
0808: ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_WIDTH,
0809: new Float(dim.getWidth()));
0810: setStyleProperty(
0811: ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_HEIGHT,
0812: new Float(dim.getHeight()));
0813: return true;
0814: }
0815: if (key == ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS) {
0816: final Dimension2D dim = (Dimension2D) value;
0817: setStyleProperty(
0818: ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_WIDTH,
0819: new Float(dim.getWidth()));
0820: setStyleProperty(
0821: ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_HEIGHT,
0822: new Float(dim.getHeight()));
0823: return true;
0824: }
0825: if (key == ElementStyleKeys.BORDER_TOP_LEFT_RADIUS) {
0826: final Dimension2D dim = (Dimension2D) value;
0827: setStyleProperty(
0828: ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_WIDTH,
0829: new Float(dim.getWidth()));
0830: setStyleProperty(
0831: ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_HEIGHT,
0832: new Float(dim.getHeight()));
0833: return true;
0834: }
0835: if (key == ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS) {
0836: final Dimension2D dim = (Dimension2D) value;
0837: setStyleProperty(
0838: ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_WIDTH,
0839: new Float(dim.getWidth()));
0840: setStyleProperty(
0841: ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_HEIGHT,
0842: new Float(dim.getHeight()));
0843: return true;
0844: }
0845: return false;
0846: }
0847:
0848: private Object handleGetLegacyKeys(final StyleKey key) {
0849: if (key == ElementStyleKeys.ABSOLUTE_POS) {
0850: final Float x = (Float) getStyleProperty(ElementStyleKeys.POS_X);
0851: final Float y = (Float) getStyleProperty(ElementStyleKeys.POS_Y);
0852: if (x == null || y == null) {
0853: return UNDEFINED_VALUE;
0854: }
0855: return new Point2D.Double(x.doubleValue(), y.doubleValue());
0856: }
0857: if (key == ElementStyleKeys.MINIMUMSIZE) {
0858: final Float w = (Float) getStyleProperty(ElementStyleKeys.MIN_WIDTH);
0859: final Float h = (Float) getStyleProperty(ElementStyleKeys.MIN_HEIGHT);
0860: if (w == null || h == null) {
0861: return UNDEFINED_VALUE;
0862: }
0863: return new FloatDimension(w.floatValue(), h.floatValue());
0864: }
0865: if (key == ElementStyleKeys.MAXIMUMSIZE) {
0866: final Float w = (Float) getStyleProperty(ElementStyleKeys.MAX_WIDTH);
0867: final Float h = (Float) getStyleProperty(ElementStyleKeys.MAX_HEIGHT);
0868: if (w == null || h == null) {
0869: return UNDEFINED_VALUE;
0870: }
0871: return new FloatDimension(w.floatValue(), h.floatValue());
0872: }
0873: if (key == ElementStyleKeys.PREFERREDSIZE) {
0874: final Float w = (Float) getStyleProperty(ElementStyleKeys.WIDTH);
0875: final Float h = (Float) getStyleProperty(ElementStyleKeys.HEIGHT);
0876: if (w == null || h == null) {
0877: return UNDEFINED_VALUE;
0878: }
0879: return new FloatDimension(w.floatValue(), h.floatValue());
0880: }
0881: if (key == ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS) {
0882: final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_WIDTH);
0883: final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_HEIGHT);
0884: if (w == null || h == null) {
0885: return UNDEFINED_VALUE;
0886: }
0887: return new FloatDimension(w.floatValue(), h.floatValue());
0888: }
0889: if (key == ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS) {
0890: final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_WIDTH);
0891: final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_HEIGHT);
0892: if (w == null || h == null) {
0893: return UNDEFINED_VALUE;
0894: }
0895: return new FloatDimension(w.floatValue(), h.floatValue());
0896: }
0897: if (key == ElementStyleKeys.BORDER_TOP_LEFT_RADIUS) {
0898: final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_WIDTH);
0899: final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_HEIGHT);
0900: if (w == null || h == null) {
0901: return UNDEFINED_VALUE;
0902: }
0903: return new FloatDimension(w.floatValue(), h.floatValue());
0904: }
0905: if (key == ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS) {
0906: final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_WIDTH);
0907: final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_HEIGHT);
0908: if (w == null || h == null) {
0909: return UNDEFINED_VALUE;
0910: }
0911: return new FloatDimension(w.floatValue(), h.floatValue());
0912: }
0913: return null;
0914: }
0915:
0916: /**
0917: * Creates and returns a copy of this object. After the cloning, the new StyleSheet is no longer registered with its
0918: * parents.
0919: *
0920: * @return a clone of this instance.
0921: * @see Cloneable
0922: */
0923: public Object clone() throws CloneNotSupportedException {
0924: try {
0925: final ElementStyleSheet sc = (ElementStyleSheet) super
0926: .clone();
0927: if (properties != null) {
0928: sc.properties = (Object[]) properties.clone();
0929: }
0930: if (propertyKeys != null) {
0931: sc.propertyKeys = (StyleKey[]) propertyKeys.clone();
0932: }
0933: //noinspection CloneCallsConstructors
0934: sc.styleChangeSupport = new StyleChangeSupport(sc);
0935: if (cachedProperties != null) {
0936: sc.cachedProperties = (Object[]) cachedProperties
0937: .clone();
0938: }
0939: parentsToCache();
0940: sc.parents.clone();
0941: sc.parents.clear();
0942: sc.parentsCached = new StyleSheetCarrier[parentsCached.length];
0943: for (int i = 0; i < parentsCached.length; i++) {
0944: final StyleSheetCarrier carrier = (StyleSheetCarrier) parentsCached[i]
0945: .clone();
0946: sc.parentsCached[i] = carrier;
0947: sc.parents.add(carrier);
0948: }
0949: sc.cascadeStyleSheet = null;
0950: sc.globalDefaultStyleSheet = globalDefaultStyleSheet;
0951: return sc;
0952: } catch (CloneNotSupportedException cne) {
0953: throw new IllegalStateException("Clone failed.");
0954: }
0955: }
0956:
0957: protected StyleSheetCarrier[] getParentReferences() {
0958: parentsToCache();
0959: return parentsCached;
0960: }
0961:
0962: /**
0963: * Clones the style-sheet. The assigned parent style sheets are not cloned. The stylesheets are not assigned to the
0964: * contained stylesheet collection, you have to reassign them manually ...
0965: *
0966: * @return the clone.
0967: */
0968: public ElementStyleSheet getCopy()
0969: throws CloneNotSupportedException {
0970: return (ElementStyleSheet) clone();
0971: }
0972:
0973: /**
0974: * Creates the cached object array for the parent element style sheets.
0975: */
0976: private void parentsToCache() {
0977: if (parentsCached == null) {
0978: parentsCached = (StyleSheetCarrier[]) parents
0979: .toArray(new StyleSheetCarrier[parents.size()]);
0980: }
0981: }
0982:
0983: /**
0984: * Checks, whether the given key is one of the keys used to define the font definition from this stylesheet.
0985: *
0986: * @param key the key that should be checked.
0987: * @return true, if the key is a font definition key, false otherwise.
0988: */
0989: private boolean isFontDefinitionProperty(final StyleKey key) {
0990: if (key == TextStyleKeys.FONT) {
0991: return true;
0992: }
0993: if (key == TextStyleKeys.FONTSIZE) {
0994: return true;
0995: }
0996: if (key == TextStyleKeys.BOLD) {
0997: return true;
0998: }
0999: if (key == TextStyleKeys.ITALIC) {
1000: return true;
1001: }
1002: if (key == TextStyleKeys.UNDERLINED) {
1003: return true;
1004: }
1005: if (key == TextStyleKeys.STRIKETHROUGH) {
1006: return true;
1007: }
1008: if (key == TextStyleKeys.EMBEDDED_FONT) {
1009: return true;
1010: }
1011: if (key == TextStyleKeys.FONTENCODING) {
1012: return true;
1013: }
1014: return false;
1015: }
1016:
1017: /**
1018: * Returns the font for this style-sheet.
1019: *
1020: * @return the font.
1021: */
1022: public FontDefinition getFontDefinitionProperty() {
1023: if (fontDefinition == null) {
1024: final String name = (String) getStyleProperty(TextStyleKeys.FONT);
1025: final int size = getIntStyleProperty(
1026: TextStyleKeys.FONTSIZE, -1);
1027: final boolean bold = getBooleanStyleProperty(TextStyleKeys.BOLD);
1028: final boolean italic = getBooleanStyleProperty(TextStyleKeys.ITALIC);
1029: final boolean underlined = getBooleanStyleProperty(TextStyleKeys.UNDERLINED);
1030: final boolean strike = getBooleanStyleProperty(TextStyleKeys.STRIKETHROUGH);
1031: final boolean embed = getBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT);
1032: final String encoding = (String) getStyleProperty(TextStyleKeys.FONTENCODING);
1033:
1034: final FontDefinition retval = new FontDefinition(name,
1035: size, bold, italic, underlined, strike, encoding,
1036: embed);
1037: if (allowCaching) {
1038: fontDefinition = retval;
1039: } else {
1040: return retval;
1041: }
1042: }
1043: return fontDefinition;
1044: }
1045:
1046: /**
1047: * Sets the font for this style-sheet.
1048: *
1049: * @param font the font (<code>null</code> not permitted).
1050: */
1051: public void setFontDefinitionProperty(final FontDefinition font) {
1052: if (font == null) {
1053: throw new NullPointerException(
1054: "ElementStyleSheet.setFontStyleProperty: font is null.");
1055: }
1056: setStyleProperty(TextStyleKeys.FONT, font.getFontName());
1057: setStyleProperty(TextStyleKeys.FONTSIZE, new Integer(font
1058: .getFontSize()));
1059: setBooleanStyleProperty(TextStyleKeys.BOLD, font.isBold());
1060: setBooleanStyleProperty(TextStyleKeys.ITALIC, font.isItalic());
1061: setBooleanStyleProperty(TextStyleKeys.UNDERLINED, font
1062: .isUnderline());
1063: setBooleanStyleProperty(TextStyleKeys.STRIKETHROUGH, font
1064: .isStrikeThrough());
1065: setBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT, font
1066: .isEmbeddedFont());
1067: setStyleProperty(TextStyleKeys.FONTENCODING, font
1068: .getFontEncoding(null));
1069: }
1070:
1071: /**
1072: * Returns an enumeration of all local property keys.
1073: *
1074: * @return an enumeration of all localy defined style property keys.
1075: */
1076: public Iterator getDefinedPropertyNames() {
1077: final ArrayList al = new ArrayList();
1078: if (propertyKeys != null) {
1079: for (int i = 0; i < propertyKeys.length; i++) {
1080: if (propertyKeys[i] != null) {
1081: al.add(propertyKeys[i]);
1082: }
1083: }
1084: }
1085: return Collections.unmodifiableList(al).iterator();
1086: }
1087:
1088: public StyleKey[] getDefinedPropertyNamesArray() {
1089: if (propertyKeys == null) {
1090: return EMPTY_KEYS;
1091: }
1092: return (StyleKey[]) propertyKeys.clone();
1093: }
1094:
1095: /**
1096: * Adds a {@link StyleChangeListener}.
1097: *
1098: * @param l the listener.
1099: */
1100: public void addListener(final StyleChangeListener l) {
1101: styleChangeSupport.addListener(l);
1102: }
1103:
1104: /**
1105: * Removes a {@link StyleChangeListener}.
1106: *
1107: * @param l the listener.
1108: */
1109: public void removeListener(final StyleChangeListener l) {
1110: styleChangeSupport.removeListener(l);
1111: }
1112:
1113: /**
1114: * Forwards a change event notification to all registered {@link StyleChangeListener} objects.
1115: *
1116: * @param source the source of the change.
1117: * @param key the style key.
1118: * @param value the new value.
1119: */
1120: public void styleChanged(final ElementStyleSheet source,
1121: final StyleKey key, final Object value) {
1122: if (source == this ) {
1123: return;
1124: }
1125:
1126: cachedData = null;
1127: changeTracker += 1;
1128:
1129: if (cachedProperties != null) {
1130: final int identifier = key.getIdentifier();
1131: if (cachedProperties.length > identifier) {
1132: cachedProperties[identifier] = value;
1133: }
1134: if (isFontDefinitionProperty(key)) {
1135: fontDefinition = null;
1136: }
1137: }
1138:
1139: styleChangeSupport.fireStyleChanged(key, value);
1140: }
1141:
1142: /**
1143: * Forwards a change event notification to all registered {@link StyleChangeListener} objects.
1144: *
1145: * @param source the source of the change.
1146: * @param key the style key.
1147: */
1148: public void styleRemoved(final ElementStyleSheet source,
1149: final StyleKey key) {
1150: if (source == this ) {
1151: return;
1152: }
1153: changeTracker += 1;
1154:
1155: if (cachedProperties != null) {
1156: final int identifier = key.getIdentifier();
1157: if (cachedProperties.length > identifier) {
1158: cachedProperties[identifier] = null;
1159: }
1160: if (isFontDefinitionProperty(key)) {
1161: fontDefinition = null;
1162: }
1163: }
1164: cachedData = null;
1165: styleChangeSupport.fireStyleRemoved(key);
1166: }
1167:
1168: /**
1169: * Helper method for serialization.
1170: *
1171: * @param out the output stream where to write the object.
1172: * @throws IOException if errors occur while writing the stream.
1173: */
1174: private void writeObject(final ObjectOutputStream out)
1175: throws IOException {
1176: out.defaultWriteObject();
1177:
1178: out.writeObject(propertyKeys);
1179: if (properties == null) {
1180: out.writeInt(0);
1181: } else {
1182: final int size = properties.length;
1183: out.writeInt(size);
1184: for (int i = 0; i < size; i++) {
1185: final Object value = properties[i];
1186: SerializerHelper.getInstance().writeObject(value, out);
1187: }
1188: }
1189: }
1190:
1191: /**
1192: * Helper method for serialization.
1193: *
1194: * @param in the input stream from where to read the serialized object.
1195: * @throws IOException when reading the stream fails.
1196: * @throws ClassNotFoundException if a class definition for a serialized object could not be found.
1197: */
1198: private void readObject(final ObjectInputStream in)
1199: throws IOException, ClassNotFoundException {
1200: styleChangeSupport = new StyleChangeSupport(this );
1201: fontDefinition = null;
1202: parentsCached = null;
1203: cachedProperties = null;
1204: cachedData = null;
1205:
1206: in.defaultReadObject();
1207: final StyleKey[] keys = (StyleKey[]) in.readObject();
1208: final int size = in.readInt();
1209: final Object[] values = new Object[size];
1210: final SerializerHelper serHelper = SerializerHelper
1211: .getInstance();
1212: for (int i = 0; i < size; i++) {
1213: values[i] = serHelper.readObject(in);
1214: }
1215: if (keys == null) {
1216: return;
1217: }
1218: final int keyCount = StyleKey.getDefinedStyleKeyCount();
1219: properties = new Object[keyCount];
1220: propertyKeys = new StyleKey[keyCount];
1221: final int maxLen = Math.min(Math.min(properties.length,
1222: keys.length), Math.min(propertyKeys.length,
1223: values.length));
1224: for (int i = 0; i < maxLen; i++) {
1225: final StyleKey key = keys[i];
1226: if (key != null) {
1227: final int identifier = key.getIdentifier();
1228: properties[identifier] = values[i];
1229: propertyKeys[identifier] = key;
1230: }
1231: }
1232: }
1233:
1234: /**
1235: * Returns true, if this stylesheet is one of the global default stylesheets. Global default stylesheets are
1236: * unmodifiable and shared among all element stylesheets.
1237: *
1238: * @return true, if this is one of the unmodifiable global default stylesheets, false otherwise.
1239: */
1240: public boolean isGlobalDefault() {
1241: return false;
1242: }
1243: }
|