Package org.pentaho.reporting.engine.classic.core.style

Source Code of org.pentaho.reporting.engine.classic.core.style.ElementStyleSheet

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.core.style;

import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import org.pentaho.reporting.libraries.base.util.FloatDimension;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.serializer.SerializerHelper;

/**
* An element style-sheet contains zero, one or many attributes that affect the appearance of report elements.  For each
* attribute, there is a predefined key that can be used to access that attribute in the style sheet.
* <p/>
* Every report element has an associated style-sheet.
* <p/>
* A style-sheet maintains a list of parent style-sheets.  If an attribute is not defined in a style-sheet, the code
* refers to the parent style-sheets to see if the attribute is defined there.
* <p/>
* All StyleSheet entries are checked against the StyleKeyDefinition for validity.
* <p/>
* As usual, this implementation is not synchronized, we need the performance during the reporting.
*
* @author Thomas Morgner
* @noinspection UnnecessaryUnboxing
*/
public abstract class ElementStyleSheet extends AbstractStyleSheet
    implements Serializable, StyleChangeListener, Cloneable
{
  /**
   * A key for the 'minimum size' of an element. This style property is not inherited from the parent band.
   *
   * @deprecated use the minimum-width and minimum-height style-keys instead.
   */
  public static final StyleKey MINIMUMSIZE = ElementStyleKeys.MINIMUMSIZE;
  /**
   * A key for the 'maximum size' of an element. This style property is not inherited from the parent band.
   *
   * @deprecated use the minimum-width and minimum-height style-keys instead.
   */
  public static final StyleKey MAXIMUMSIZE = ElementStyleKeys.MAXIMUMSIZE;

  /**
   * A key for the 'preferred size' of an element. This style property is not inherited from the parent band.
   *
   * @deprecated use the minimum-width and minimum-height style-keys instead.
   */
  public static final StyleKey PREFERREDSIZE = ElementStyleKeys.PREFERREDSIZE;

  /**
   * A key for an element's 'visible' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey VISIBLE = ElementStyleKeys.VISIBLE;

  /**
   * A key for the 'paint' used to color an element. For historical reasons, this key requires a color value.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey PAINT = ElementStyleKeys.PAINT;

  /**
   * A key for the 'stroke' used to draw an element. (This now only applies to shape and drawable-elements.)
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey STROKE = ElementStyleKeys.STROKE;

  /**
   * A key for the horizontal alignment of an element.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey ALIGNMENT = ElementStyleKeys.ALIGNMENT;

  /**
   * A key for the vertical alignment of an element.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey VALIGNMENT = ElementStyleKeys.VALIGNMENT;

  /**
   * A key for an element's 'scale' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey SCALE = ElementStyleKeys.SCALE;

  /**
   * A key for an element's 'keep aspect ratio' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey KEEP_ASPECT_RATIO = ElementStyleKeys.KEEP_ASPECT_RATIO;

  /**
   * A key for the dynamic height flag for an element.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey DYNAMIC_HEIGHT = ElementStyleKeys.DYNAMIC_HEIGHT;

  /**
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey HREF_TARGET = ElementStyleKeys.HREF_TARGET;

  /**
   * Specifies the anchor tag's target window for opening the link.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey HREF_WINDOW = ElementStyleKeys.HREF_WINDOW;

  /**
   * The StyleKey for the user defined cell data format.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey EXCEL_WRAP_TEXT = ElementStyleKeys.EXCEL_WRAP_TEXT;

  /**
   * The StyleKey for the user defined cell data format.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey EXCEL_DATA_FORMAT_STRING = ElementStyleKeys.EXCEL_DATA_FORMAT_STRING;

  /**
   * A key for the 'font family' used to draw element text.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey FONT = TextStyleKeys.FONT;

  /**
   * A key for the 'font size' used to draw element text.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey FONTSIZE = TextStyleKeys.FONTSIZE;

  /**
   * A key for the 'font size' used to draw element text.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey LINEHEIGHT = TextStyleKeys.LINEHEIGHT;

  /**
   * A key for an element's 'bold' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey BOLD = TextStyleKeys.BOLD;

  /**
   * A key for an element's 'italic' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey ITALIC = TextStyleKeys.ITALIC;

  /**
   * A key for an element's 'underlined' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey UNDERLINED = TextStyleKeys.UNDERLINED;

  /**
   * A key for an element's 'strikethrough' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey STRIKETHROUGH = TextStyleKeys.STRIKETHROUGH;

  /**
   * A key for an element's 'embedd' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey EMBEDDED_FONT = TextStyleKeys.EMBEDDED_FONT;

  /**
   * A key for an element's 'embedd' flag.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey FONTENCODING = TextStyleKeys.FONTENCODING;

  /**
   * The string that is used to end a text if not all text fits into the element.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey RESERVED_LITERAL = TextStyleKeys.RESERVED_LITERAL;

  /**
   * The Layout Cacheable stylekey. Set this stylekey to false, to define that the element is not cachable. This key
   * defaults to true.
   *
   * @deprecated use ElementStyleKeys instead..
   */
  public static final StyleKey TRIM_TEXT_CONTENT = TextStyleKeys.TRIM_TEXT_CONTENT;

  /**
   * A singleton marker for the cache.
   */
  private static final Object UNDEFINED_VALUE = new Object();

  /**
   * The style-sheet name.
   */
  private String name;

  /**
   * Storage for the parent style sheets (if any).
   */
  private ArrayList<StyleSheetCarrier> parents;

  /**
   * Storage for readonly style sheets.
   */
  private ElementDefaultStyleSheet globalDefaultStyleSheet;
  private ElementStyleSheet cascadeStyleSheet;

  /**
   * Parent style sheet cache.
   */
  private transient StyleSheetCarrier[] parentsCached;

  /**
   * The keys for the properties that have been explicitly set on the element.
   */
  private StyleKey[] propertyKeys;

  /**
   * The properties that have been explicitly set on the element.
   */
  private transient Object[] properties;
  private byte[] source;

  private static final byte SOURCE_UNDEFINED = 0;
  private static final byte SOURCE_FROM_PARENT = 1;
  private static final byte SOURCE_DIRECT = 2;

  /**
   * Style change support.
   */
  private transient StyleChangeSupport styleChangeSupport;

  private long changeTracker;
  private static final StyleKey[] EMPTY_KEYS = new StyleKey[0];
  private static final StyleSheetCarrier[] EMPTY_PARENTS = new StyleSheetCarrier[0];

  /**
   * Creates a new element style-sheet with the given name.  The style-sheet initially contains no attributes, and has
   * no parent style-sheets.
   *
   * @param name the name (<code>null</code> not permitted).
   */
  protected ElementStyleSheet(final String name)
  {
    if (name == null)
    {
      throw new NullPointerException("ElementStyleSheet constructor: name is null.");
    }
    this.name = name;
    this.styleChangeSupport = new StyleChangeSupport(this);
    this.propertyKeys = StyleKey.getDefinedStyleKeys();
    if (propertyKeys[0] == null)
    {
      throw new IllegalStateException();
    }
  }

  /**
   * Creates a new element style-sheet with the given name.  The style-sheet initially contains no attributes, and has
   * no parent style-sheets.
   *
   * @param name the name (<code>null</code> not permitted).
   * @deprecated no longer used. Will be removed in the next iteration.
   */
  protected ElementStyleSheet(final String name,
                              final ElementStyleSheet deriveParent)
  {
    if (name == null)
    {
      throw new NullPointerException("ElementStyleSheet constructor: name is null.");
    }
    if (deriveParent == null)
    {
      throw new NullPointerException("ElementStyleSheet constructor: parent cannot be null.");
    }
    this.name = name;
    this.styleChangeSupport = new StyleChangeSupport(this);
    // These arrays are immutable ..
    this.propertyKeys = deriveParent.propertyKeys;
    if (propertyKeys[0] == null)
    {
      throw new IllegalStateException();
    }
    this.properties = new Object[propertyKeys.length];
    this.source = new byte[propertyKeys.length];
    // Cached properties are not always needed ..
    this.changeTracker = deriveParent.changeTracker;
  }

  /**
   * Returns <code>true</code> if caching is allowed, and <code>false</code> otherwise.
   *
   * @return A boolean.
   * @deprecated has no effect. Always return true.
   */
  public final boolean isAllowCaching()
  {
    return true;
  }

  public long getChangeTracker()
  {
    return changeTracker;
  }

  /**
   * Returns true, if the given key is locally defined, false otherwise.
   *
   * @param key the key to test
   * @return true, if the key is local, false otherwise.
   */
  public boolean isLocalKey(final StyleKey key)
  {
    if (source == null)
    {
      return false;
    }
    final int identifier = key.identifier;
    if (source.length <= identifier)
    {
      return false;
    }
    return source[identifier] == SOURCE_DIRECT;
  }

  /**
   * Sets the flag that controls whether or not caching is allowed.
   *
   * @param allowCaching the flag value.
   * @deprecated has no effect - there is always some caching now
   */
  public void setAllowCaching(final boolean allowCaching)
  {
  }

  /**
   * Returns the name of the style-sheet.
   *
   * @return the name (never <code>null</code>).
   */
  public String getName()
  {
    return name;
  }

  /**
   * Adds a parent style-sheet. This method adds the parent to the beginning of the list, and guarantees, that this
   * parent is queried first.
   *
   * @param parent the parent (<code>null</code> not permitted).
   */
  public void addParent(final ElementStyleSheet parent)
  {
    addParent(0, parent);
  }

  /**
   * Adds a parent style-sheet. Parents on a lower position are queried before any parent with an higher position in the
   * list.
   *
   * @param position the position where to insert the parent style sheet
   * @param parent   the parent (<code>null</code> not permitted).
   * @throws IndexOutOfBoundsException if the position is invalid (pos &lt; 0 or pos &gt;= numberOfParents)
   */
  public void addParent(final int position, final ElementStyleSheet parent)
  {
    if (parent == null)
    {
      throw new NullPointerException("ElementStyleSheet.addParent(...): parent is null.");
    }
    if (parent.isSubStyleSheet(this) == false)
    {
      final StyleSheetCarrier carrier = createCarrier(parent);
      if (carrier == null)
      {
        throw new IllegalArgumentException
            ("The given StyleSheet cannot be added to this stylesheet.");
      }
      if (parents == null)
      {
        parents = new ArrayList<StyleSheetCarrier>();
      }
      parents.add(position, carrier);
      parentsCached = null;
      changeTracker += 1;
    }
    else
    {
      throw new IllegalArgumentException("Cannot add parent as child.");
    }
  }

  protected abstract StyleSheetCarrier createCarrier(ElementStyleSheet styleSheet);


  /**
   * Checks whether the given element stylesheet is already added as child into the stylesheet tree.
   *
   * @param parent the element that should be tested.
   * @return true, if the element is a child of this element style sheet, false otherwise.
   */
  protected boolean isSubStyleSheet(final ElementStyleSheet parent)
  {
    if (parents == null)
    {
      return false;
    }
    final int size = parents.size();
    for (int i = 0; i < size; i++)
    {
      final StyleSheetCarrier ca = (StyleSheetCarrier) parents.get(i);
      final ElementStyleSheet es = ca.getStyleSheet();
      if (es == parent)
      {
        return true;
      }
      if (es.isSubStyleSheet(parent) == true)
      {
        return true;
      }
    }
    return false;
  }

  /**
   * Removes a parent style-sheet.
   *
   * @param parent the style-sheet to remove (<code>null</code> not permitted).
   */
  public void removeParent(final ElementStyleSheet parent)
  {
    if (parent == null)
    {
      throw new NullPointerException("ElementStyleSheet.removeParent(...): parent is null.");
    }
    if (parents == null)
    {
      return;
    }
    final Iterator it = parents.iterator();
    while (it.hasNext())
    {
      final StyleSheetCarrier carrier = (StyleSheetCarrier) it.next();
      if (carrier.isSame(parent))
      {
        it.remove();
        carrier.invalidate();
        parentsCached = null;
        changeTracker += 1;
      }
    }
  }

  /**
   * Returns a list of the parent style-sheets.
   * <p/>
   * The list is unmodifiable.
   *
   * @return the list.
   */
  public ElementStyleSheet[] getParents()
  {
    if (parentsCached == null)
    {
      this.parentsToCache();
    }
    final ElementStyleSheet[] styleSheets = new ElementStyleSheet[parentsCached.length];
    for (int i = 0; i < styleSheets.length; i++)
    {
      styleSheets[i] = parentsCached[i].getStyleSheet();
    }
    return styleSheets;
  }

  /**
   * Returns the global default (if defined).
   *
   * @return the list.
   */
  public ElementDefaultStyleSheet getGlobalDefaultStyleSheet()
  {
    return globalDefaultStyleSheet;
  }

  public void setGlobalDefaultStyleSheet(final ElementDefaultStyleSheet defaultStyleSheet)
  {
    this.globalDefaultStyleSheet = defaultStyleSheet;
  }

  public ElementStyleSheet getCascadeStyleSheet()
  {
    return cascadeStyleSheet;
  }

  public void setCascadeStyleSheet(final ElementStyleSheet cascadeStyleSheet)
  {
    if (this.cascadeStyleSheet != null)
    {
      this.cascadeStyleSheet.removeListener(this);
    }
    this.cascadeStyleSheet = cascadeStyleSheet;
    if (this.cascadeStyleSheet != null)
    {
      this.cascadeStyleSheet.addListener(this);
    }
  }

  public final Object[] toArray()
  {
    final StyleKey[] keys = propertyKeys;
    final Object[] data = new Object[keys.length];
    if (source == null)
    {
      source = new byte[keys.length];
      properties = new Object[keys.length];
    }

    for (int i = 0; i < keys.length; i++)
    {
      final StyleKey key = keys[i];
      if (key == null)
      {
        throw new NullPointerException();
      }
      final int identifier = key.identifier;
      final byte sourceHint = source[identifier];
      if (sourceHint == SOURCE_UNDEFINED)
      {
        data[identifier] = getStyleProperty(key);
      }
      else
      {
        data[identifier] = properties[identifier];
      }
    }
    return data;
  }

  /**
   * Returns the value of a style.  If the style is not found in this style-sheet, the code looks in the parent
   * style-sheets.  If the style is not found in any of the parent style-sheets, then the default value (possibly
   * <code>null</code>) is returned.
   *
   * @param key          the style key.
   * @param defaultValue the default value (<code>null</code> permitted).
   * @return the value.
   */
  public Object getStyleProperty(final StyleKey key, final Object defaultValue)
  {
    final Object legacyVal = handleGetLegacyKeys(key);
    if (legacyVal == ElementStyleSheet.UNDEFINED_VALUE)
    {
      return defaultValue;
    }
    else if (legacyVal != null)
    {
      return legacyVal;
    }

    final int identifier = key.identifier;
    if (properties != null)
    {
      if (properties.length <= identifier)
      {
        throw new IllegalStateException();
      }

      final byte source = this.source[identifier];
      if (source != SOURCE_UNDEFINED)
      {
        final Object value = properties[identifier];
        if (value == null)
        {
          return defaultValue;
        }
        return value;
      }
    }

    // parents must always be queried ...
    parentsToCache();
    for (int i = 0; i < parentsCached.length; i++)
    {
      final ElementStyleSheet st = parentsCached[i].getStyleSheet();
      final Object value = st.getStyleProperty(key, null);
      if (value == null)
      {
        continue;
      }
      putInCache(key, value, SOURCE_FROM_PARENT);
      return value;
    }

    if (key.isInheritable())
    {
      if (cascadeStyleSheet != null)
      {
        final Object value = cascadeStyleSheet.getStyleProperty(key, null);
        if (value != null)
        {
          putInCache(key, value, SOURCE_FROM_PARENT);
          return value;
        }
      }
    }

    if (globalDefaultStyleSheet != null)
    {
      final Object value = globalDefaultStyleSheet.getStyleProperty(key, null);
      if (value != null)
      {
        putInCache(key, value, SOURCE_FROM_PARENT);
        return value;
      }
    }

    putInCache(key, null, SOURCE_FROM_PARENT);
    return defaultValue;
  }

  /**
   * Puts an object into the cache (if caching is enabled).
   *
   * @param key   the stylekey for that object
   * @param value the object.
   */
  private void putInCache(final StyleKey key, final Object value, final byte sourceHint)
  {
    if (properties == null)
    {
      final int definedStyleKeyCount = propertyKeys.length;
      properties = new Object[definedStyleKeyCount];
      source = new byte[definedStyleKeyCount];
    }

    final int identifier = key.identifier;
    properties[identifier] = value;
    source[identifier] = sourceHint;
  }

  /**
   * Sets a boolean style property.
   *
   * @param key   the style key (<code>null</code> not permitted).
   * @param value the value.
   * @throws NullPointerException if the given key is null.
   * @throws ClassCastException   if the value cannot be assigned with the given key.
   */
  public void setBooleanStyleProperty(final StyleKey key, final boolean value)
  {
    if (value)
    {
      setStyleProperty(key, Boolean.TRUE);
    }
    else
    {
      setStyleProperty(key, Boolean.FALSE);
    }
  }

  /**
   * Sets a style property (or removes the style if the value is <code>null</code>).
   *
   * @param key   the style key (<code>null</code> not permitted).
   * @param value the value.
   * @throws NullPointerException if the given key is null.
   * @throws ClassCastException   if the value cannot be assigned with the given key.
   */
  public void setStyleProperty(final StyleKey key, final Object value)
  {
    if (key == null)
    {
      throw new NullPointerException("ElementStyleSheet.setStyleProperty: key is null.");
    }
    if (handleSetLegacyKeys(key, value))
    {
      return;
    }

    final int identifier = key.identifier;
    if (value == null)
    {
      if (properties != null)
      {
        if (properties[identifier] == null)
        {
          return;
        }

        // invalidate the cache ..
        putInCache(key, null, SOURCE_UNDEFINED);

        changeTracker += 1;
        properties[identifier] = null;
        styleChangeSupport.fireStyleRemoved(key);
      }
      return;
    }

    if (key.getValueType().isAssignableFrom(value.getClass()) == false)
    {
      throw new ClassCastException("Value for key " + key.getName()
          + " is not assignable: " + value.getClass()
          + " is not assignable from " + key.getValueType());
    }
    if (properties == null)
    {
      final int definedStyleKeyCount = propertyKeys.length;
      properties = new Object[definedStyleKeyCount];
      source = new byte[definedStyleKeyCount];
    }

    if (ObjectUtilities.equal(properties[identifier], value))
    {
      // no need to change anything ..
      return;
    }

    // invalidate the cache ..
    putInCache(key, value, SOURCE_DIRECT);
    changeTracker += 1;

    styleChangeSupport.fireStyleChanged(key, value);
  }

  private boolean handleSetLegacyKeys(final StyleKey key, final Object value)
  {
    if (value == null)
    {
      return false;
    }
    if (key == ElementStyleKeys.ABSOLUTE_POS)
    {
      final Point2D point = (Point2D) value;
      setStyleProperty(ElementStyleKeys.POS_X, new Float(point.getX()));
      setStyleProperty(ElementStyleKeys.POS_Y, new Float(point.getY()));
      return true;
    }
    if (key == ElementStyleKeys.MINIMUMSIZE)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.MIN_WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.MIN_HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    if (key == ElementStyleKeys.MAXIMUMSIZE)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.MAX_WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.MAX_HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    if (key == ElementStyleKeys.PREFERREDSIZE)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    if (key == ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    if (key == ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    if (key == ElementStyleKeys.BORDER_TOP_LEFT_RADIUS)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    if (key == ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS)
    {
      final Dimension2D dim = (Dimension2D) value;
      setStyleProperty(ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_WIDTH, new Float(dim.getWidth()));
      setStyleProperty(ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_HEIGHT, new Float(dim.getHeight()));
      return true;
    }
    return false;
  }

  private Object handleGetLegacyKeys(final StyleKey key)
  {
    if (key == ElementStyleKeys.ABSOLUTE_POS)
    {
      final Float x = (Float) getStyleProperty(ElementStyleKeys.POS_X);
      final Float y = (Float) getStyleProperty(ElementStyleKeys.POS_Y);
      if (x == null || y == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new Point2D.Double(x.doubleValue(), y.doubleValue());
    }
    if (key == ElementStyleKeys.MINIMUMSIZE)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.MIN_WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.MIN_HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    if (key == ElementStyleKeys.MAXIMUMSIZE)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.MAX_WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.MAX_HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    if (key == ElementStyleKeys.PREFERREDSIZE)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    if (key == ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    if (key == ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    if (key == ElementStyleKeys.BORDER_TOP_LEFT_RADIUS)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    if (key == ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS)
    {
      final Float w = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_WIDTH);
      final Float h = (Float) getStyleProperty(ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_HEIGHT);
      if (w == null || h == null)
      {
        return ElementStyleSheet.UNDEFINED_VALUE;
      }
      return new FloatDimension(w.floatValue(), h.floatValue());
    }
    return null;
  }

  /**
   * Creates and returns a copy of this object. After the cloning, the new StyleSheet is no longer registered with its
   * parents.
   *
   * @return a clone of this instance.
   * @see Cloneable
   */
  public Object clone()
      throws CloneNotSupportedException
  {
    try
    {
      final ElementStyleSheet sc = (ElementStyleSheet) super.clone();
      if (properties != null)
      {
        sc.properties = properties.clone();
      }
      if (source != null)
      {
        sc.source = source.clone();
      }
      //noinspection CloneCallsConstructors
      sc.styleChangeSupport = new StyleChangeSupport(sc);
      if (sc.parents != null)
      {
        parentsToCache();
        sc.parents = (ArrayList<StyleSheetCarrier>) parents.clone();
        sc.parents.clear();
        sc.parentsCached = new StyleSheetCarrier[parentsCached.length];
        for (int i = 0; i < parentsCached.length; i++)
        {
          final StyleSheetCarrier carrier = (StyleSheetCarrier) parentsCached[i].clone();
          sc.parentsCached[i] = carrier;
          sc.parents.add(carrier);
        }
      }
      sc.cascadeStyleSheet = null;
      sc.globalDefaultStyleSheet = globalDefaultStyleSheet;
      return sc;
    }
    catch (CloneNotSupportedException cne)
    {
      throw new IllegalStateException("Clone failed.");
    }
  }

  public StyleSheet derive()
      throws CloneNotSupportedException
  {
    try
    {
      final ElementStyleSheet sc = (ElementStyleSheet) super.derive();
      if (properties != null)
      {
        sc.properties = properties.clone();
      }
      if (source != null)
      {
        sc.source = source.clone();
      }
      //noinspection CloneCallsConstructors
      sc.styleChangeSupport = new StyleChangeSupport(sc);
      if (sc.parents != null)
      {
        parentsToCache();
        sc.parents = (ArrayList<StyleSheetCarrier>) parents.clone();
        sc.parents.clear();
        sc.parentsCached = new StyleSheetCarrier[parentsCached.length];
        for (int i = 0; i < parentsCached.length; i++)
        {
          final StyleSheetCarrier carrier = (StyleSheetCarrier) parentsCached[i].clone();
          sc.parentsCached[i] = carrier;
          sc.parents.add(carrier);
        }
      }
      sc.cascadeStyleSheet = null;
      sc.globalDefaultStyleSheet = globalDefaultStyleSheet;
      return sc;
    }
    catch (final CloneNotSupportedException cne)
    {
      throw new IllegalStateException("Clone failed.");
    }
  }

  protected StyleSheetCarrier[] getParentReferences()
  {
    parentsToCache();
    return parentsCached;
  }

  /**
   * Clones the style-sheet. The assigned parent style sheets are not cloned. The stylesheets are not assigned to the
   * contained stylesheet collection, you have to reassign them manually ...
   *
   * @return the clone.
   */
  public ElementStyleSheet getCopy()
      throws CloneNotSupportedException
  {
    return (ElementStyleSheet) clone();
  }

  /**
   * Creates the cached object array for the parent element style sheets.
   */
  private void parentsToCache()
  {
    if (parents == null)
    {
      parentsCached = ElementStyleSheet.EMPTY_PARENTS;
      return;
    }

    if (parentsCached == null)
    {
      parentsCached = (StyleSheetCarrier[])
          parents.toArray(new StyleSheetCarrier[parents.size()]);
    }
  }

  /**
   * Returns the font for this style-sheet.
   *
   * @return the font.
   * @deprecated This method will be removed in the next version.
   */
  public FontDefinition getFontDefinitionProperty()
  {
    final String name = (String) getStyleProperty(TextStyleKeys.FONT);
    final int size = getIntStyleProperty(TextStyleKeys.FONTSIZE, -1);
    final boolean bold = getBooleanStyleProperty(TextStyleKeys.BOLD);
    final boolean italic = getBooleanStyleProperty(TextStyleKeys.ITALIC);
    final boolean underlined = getBooleanStyleProperty(TextStyleKeys.UNDERLINED);
    final boolean strike = getBooleanStyleProperty(TextStyleKeys.STRIKETHROUGH);
    final boolean embed = getBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT);
    final String encoding = (String) getStyleProperty(TextStyleKeys.FONTENCODING);

    return new FontDefinition(name, size, bold, italic, underlined, strike,
        encoding, embed);
  }

  /**
   * Sets the font for this style-sheet.
   *
   * @param font the font (<code>null</code> not permitted).
   * @deprecated This method will be removed in the next version.
   */
  public void setFontDefinitionProperty(final FontDefinition font)
  {
    if (font == null)
    {
      throw new NullPointerException("ElementStyleSheet.setFontStyleProperty: font is null.");
    }
    setStyleProperty(TextStyleKeys.FONT, font.getFontName());
    setStyleProperty(TextStyleKeys.FONTSIZE, new Integer(font.getFontSize()));
    setBooleanStyleProperty(TextStyleKeys.BOLD, font.isBold());
    setBooleanStyleProperty(TextStyleKeys.ITALIC, font.isItalic());
    setBooleanStyleProperty(TextStyleKeys.UNDERLINED, font.isUnderline());
    setBooleanStyleProperty(TextStyleKeys.STRIKETHROUGH, font.isStrikeThrough());
    setBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT, font.isEmbeddedFont());
    setStyleProperty(TextStyleKeys.FONTENCODING, font.getFontEncoding(null));
  }

  /**
   * Returns an enumeration of all local property keys.
   *
   * @return an enumeration of all localy defined style property keys.
   */
  public Iterator getDefinedPropertyNames()
  {
    final ArrayList al = new ArrayList();
    if (source != null)
    {
      for (int i = 0; i < source.length; i++)
      {
        if (source[i] == SOURCE_DIRECT)
        {
          al.add(propertyKeys[i]);
        }
      }
    }
    return Collections.unmodifiableList(al).iterator();
  }

  public StyleKey[] getDefinedPropertyNamesArray()
  {
    if (source == null)
    {
      return ElementStyleSheet.EMPTY_KEYS;
    }

    final StyleKey[] retval = propertyKeys.clone();
    for (int i = 0; i < source.length; i++)
    {
      if (source[i] != SOURCE_DIRECT)
      {
        retval[i] = null;
      }
    }
    return retval;
  }

  /**
   * Adds a {@link StyleChangeListener}.
   *
   * @param l the listener.
   */
  public void addListener(final StyleChangeListener l)
  {
    styleChangeSupport.addListener(l);
  }

  /**
   * Removes a {@link StyleChangeListener}.
   *
   * @param l the listener.
   */
  public void removeListener(final StyleChangeListener l)
  {
    styleChangeSupport.removeListener(l);
  }

  /**
   * Forwards a change event notification to all registered {@link StyleChangeListener} objects.
   *
   * @param source the source of the change.
   * @param key    the style key.
   * @param value  the new value.
   */
  public void styleChanged(final ElementStyleSheet source, final StyleKey key,
                           final Object value)
  {
    if (source == this)
    {
      return;
    }

    if (key.isInheritable() == false)
    {
      return;
    }

    if (source != null && this.source != null)
    {
      if (this.source[key.identifier] == SOURCE_DIRECT)
      {
        return;
      }
    }

    changeTracker += 1;
    putInCache(key, value, SOURCE_FROM_PARENT);

    styleChangeSupport.fireStyleChanged(key, value);
  }

  /**
   * Forwards a change event notification to all registered {@link StyleChangeListener} objects.
   *
   * @param source the source of the change.
   * @param key    the style key.
   */
  public void styleRemoved(final ElementStyleSheet source, final StyleKey key)
  {
    if (source == this)
    {
      return;
    }
    if (key.isInheritable() == false)
    {
      return;
    }
    if (source != null && this.source != null)
    {
      if (this.source[key.identifier] == SOURCE_DIRECT)
      {
        return;
      }
    }

    changeTracker += 1;
    putInCache(key, null, SOURCE_UNDEFINED);
    styleChangeSupport.fireStyleRemoved(key);
  }

  /**
   * Helper method for serialization.
   *
   * @param out the output stream where to write the object.
   * @throws IOException if errors occur while writing the stream.
   */
  private void writeObject(final ObjectOutputStream out)
      throws IOException
  {
    out.defaultWriteObject();
    if (properties == null)
    {
      out.writeInt(0);
    }
    else
    {
      final int size = properties.length;
      out.writeInt(size);
      for (int i = 0; i < size; i++)
      {
        final Object value = properties[i];
        SerializerHelper.getInstance().writeObject(value, out);
      }
    }
  }

  /**
   * Helper method for serialization.
   *
   * @param in the input stream from where to read the serialized object.
   * @throws IOException            when reading the stream fails.
   * @throws ClassNotFoundException if a class definition for a serialized object could not be found.
   */
  private void readObject(final ObjectInputStream in)
      throws IOException, ClassNotFoundException
  {
    in.defaultReadObject();
    final int size = in.readInt();

    propertyKeys = StyleKey.getDefinedStyleKeys();
    styleChangeSupport = new StyleChangeSupport(this);
    parentsCached = null;

    if (size == 0)
    {
      properties = null;
      return;
    }

    if (size != propertyKeys.length)
    {
      throw new IOException("Encountered a different style-system configuration. This report cannot be deserialized.");
    }
    if (propertyKeys[0] == null)
    {
      throw new IllegalStateException();
    }
    properties = new Object[size];

    if (size == 0)
    {
      return;
    }
    final Object[] values = new Object[size];
    final SerializerHelper serHelper = SerializerHelper.getInstance();
    for (int i = 0; i < size; i++)
    {
      values[i] = serHelper.readObject(in);
    }

    for (int i = 0; i < size; i++)
    {
      final StyleKey key = propertyKeys[i];
      if (key != null)
      {
        final int identifier = key.identifier;
        properties[identifier] = values[i];
      }
    }
  }

  /**
   * Returns true, if this stylesheet is one of the global default stylesheets. Global default stylesheets are
   * unmodifiable and shared among all element stylesheets.
   *
   * @return true, if this is one of the unmodifiable global default stylesheets, false otherwise.
   */
  public boolean isGlobalDefault()
  {
    return false;
  }

  /**
   * Returns the property keys. This must return the same set of keys as a call to StyleSheet.getDefinedKeys(),
   * but it allows us to avoid the synchronization on that call.
   *
   * @return the local copy of the style keys.
   */
  public StyleKey[] getPropertyKeys()
  {
    return propertyKeys.clone();
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.style.ElementStyleSheet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.