/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.fop.fo; import java.lang.Character; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.List; import java.util.HashSet; import java.util.LinkedList; import java.util.BitSet; import java.util.StringTokenizer; import org.apache.fop.fo.FOTree; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropNames; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.datatypes.Numeric; import org.apache.fop.datatypes.Ints; import org.apache.fop.datastructs.ROIntArray; import org.apache.fop.datastructs.ROStringArray; import org.apache.fop.datastructs.ROBitSet; import org.apache.fop.datatypes.PropertyValue; /** * This class contains a number of arrays containing values indexed by the * property index value, determined from the PropNames class. These arrays * provide a means of accessing information about the nature of a property * through the property index value. * <p>Most of these arrays are initialised piecemeal as information is * required about a particular property. * There are also <tt>HashMap</tt>s which encode the various sets of * properties which are defined to apply to each of the Flow Objects, * and a <tt>BitSet</tt> of those properties which are * automatically inherited. The <tt>HashMap</tt>s provide a convenient * means of specifying the relationship between FOs and properties. * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a> */ public class PropertyConsts { private static final String tag = "$Name$"; private static final String revision = "$Revision$"; private static final String packageName = "org.apache.fop.fo"; public static final PropertyConsts pconsts; static { try { pconsts = new PropertyConsts(); } catch (PropertyException e) { throw new RuntimeException(e.getMessage()); } } public static final PropertyConsts getPropertyConsts() { return pconsts; } /** * A Property[] array containing Property objects corresponding to each * of the property indices in <tt>PropNames</tt>. * Initially empty, entries are filled on demand as calls for details * about individual properties are made. */ private final Property[] properties = new Property[PropNames.LAST_PROPERTY_INDEX + 1]; /** * A Class[] array containing Class objects corresponding to each of the * class names in the classNames array. Elements are set * in parallel to the creation of the class names in * the classNames array. It can be indexed by the property name * constants defined in this file. */ private final Class[] classes = new Class[PropNames.LAST_PROPERTY_INDEX + 1]; /** * A String[] array of the property class names. This array is * effectively 1-based, with the first element being unused. * The elements of this array are set by converting the FO * property names from the array PropNames.propertyNames into class * names by converting the first character of every component word to * upper case, and removing all punctuation characters. * It can be indexed by the property name constants defined in * the PropNames class. */ private final String[] classNames = new String[PropNames.LAST_PROPERTY_INDEX + 1]; /** * A HashMap whose elements are an integer index value keyed by the name * of a property class. The index value is the index of the property * class name in the classNames[] array. */ private final HashMap classToIndex = new HashMap(PropNames.LAST_PROPERTY_INDEX + 1); /** * An <tt>int[]</tt> containing the <i>inherited</i> values from the * <tt>Property</tt> classes. */ private final int[] inherited = new int[PropNames.LAST_PROPERTY_INDEX + 1]; /** * A <tt>BitSet</tt> of properties which are normally inherited * (strictly, not not inherited). * It is defined relative to the set of all properties; i.e. the * inheritability of any property can be established by testing the * bit in this set that corresponds to the queried property's index. * <p>The <tt>BitSet</tt> is private and is the basis for * <i>inheritedProperties</i>. */ private final BitSet inheritedprops = new BitSet(PropNames.LAST_PROPERTY_INDEX + 1); /** * An int[] array of the types of the <i>initialValue</i> field of each * property. The array is indexed by the index value constants that are * defined in the PropNames class in parallel to the * PropNames.propertyNames[] array. */ private final int[] initialValueTypes = new int[PropNames.LAST_PROPERTY_INDEX + 1]; /** * A <tt>PropertyValue</tt> array containing the initial values of * each of the properties. */ private final PropertyValue[] initialValues = new PropertyValue[PropNames.LAST_PROPERTY_INDEX + 1]; /** * An int[] array of the values of the <i>dataTypes</i> field of each * property. The array is indexed by the index value constants that are * defined in the PropNames class in parallel to the * PropNames.propertyNames[] array. * The array elements are set from the values of the * <i>dataTypes</i> field in each property class. */ private final int[] datatypes = new int[PropNames.LAST_PROPERTY_INDEX + 1]; /** * An int[] array of the values of the <i>traitMapping</i> field of each * property. The array is indexed by the index value constants that are * defined in the PropNames class in parallel to the * PropNames.propertyNames[] array. * The array elements are set from the values of the * <i>traitMapping</i> field in each property class. */ private final int[] traitMappings = new int[PropNames.LAST_PROPERTY_INDEX + 1]; /** * Get the initial value type for a property name. * @param property String name of the FO property * @return int enumerated initialValueType. These constants are defined * as static final ints in this class. Note that an undefined property * name will return the constant defined as NOTYPE_IT */ public int getInitialValueType(String property) throws PropertyException { // Get the property index then index into the initialvaluetypes array return getInitialValueType(PropNames.getPropertyIndex(property)); } /** * get the initial value type for a property index. * @param propindex int index of the FO property * @return int enumerated initialValueType. These constants are defined * as static final ints in the Property class. * @throws PropertyException */ public int getInitialValueType(int propindex) throws PropertyException { setupProperty(propindex); //System.out.println("getInitialValueType: " + propindex + " " //+ initialValueTypes[propindex]); return initialValueTypes[propindex]; } /** * Get the initial value <tt>PropertyValue</tt> for a given property. * Note that this is a <b>raw</b> value; if it is * an unresolved percentage that value will be returned. * @param index - the property index. * @return a <tt>PropertyValue</tt> containing the initial property * value element for the indexed property. * @exception <tt>PropertyException</tt> */ public PropertyValue getInitialValue(int propindex) throws PropertyException { if (initialValues[propindex] != null) return initialValues[propindex]; //System.out.println("PropertyConts.getInitialValue(" + propindex //+ ") " + PropNames.getPropertyName(propindex)); return (initialValues[propindex] = setupProperty(propindex).getInitialValue(propindex)); } /** * @param propindex <tt>int</tt> index of the property * @param foNode the node whose properties are being constructed. * @param value the <tt>PropertyValue</tt> being refined. * @return <tt>PropertyValue</tt> constructed by the property's * <i>refineParsing</i> method * @exception <tt>PropertyException</tt> */ public PropertyValue refineParsing (int propindex, FONode foNode, PropertyValue value) throws PropertyException { Property property = setupProperty(propindex); return property.refineParsing(propindex, foNode, value); } /** * @param propindex <tt>int</tt> index of the property * @param foNode the node whose properties are being constructed. * @param value the <tt>PropertyValue</tt> being refined. * @param nested - <tt>boolean</tt> indicating whether this method is * called normally (false), or as part of another <i>refineParsing</i> * method. * @see #refineParsing(FOTree,PropertyValue) * @return <tt>PropertyValue</tt> constructed by the property's * <i>refineParsing</i> method * @exception <tt>PropertyException</tt> */ public PropertyValue refineParsing (int propindex, FONode foNode, PropertyValue value, boolean isNested) throws PropertyException { Property property = setupProperty(propindex); return property.refineParsing(propindex, foNode, value, isNested); } /** * Get the <tt>Numeric</tt> value corresponding to an enumerated value. * @param foNode the <tt>FONode</tt> being built * @param propindex int index of the FO property * @param enum - the integer equivalent of the enumeration keyword. * @return the <tt>Numeric</tt> result. * @throws PropertyException. */ public Numeric getMappedNumeric(FONode foNode, int propindex, int enum) throws PropertyException { Property property = setupProperty(propindex); if ((datatypes[propindex] & Property.MAPPED_LENGTH) != 0) return property.getMappedLength(foNode, enum); else throw new PropertyException ("MAPPED_LENGTH not valid in " + PropNames.getPropertyName(propindex)); } /** * @param propindex int index of the FO property * @return int type of inheritance for this property * (See constants defined in Properties.) * @throws PropertyException. */ public int inheritance(String property) throws PropertyException { return inheritance(PropNames.getPropertyIndex(property)); } /** * @param propindex int index of the FO property * @return int type of inheritance for this property * (See constants defined in Property.) * @throws PropertyException. */ public int inheritance(int propindex) throws PropertyException { setupProperty(propindex); return inherited[propindex]; } /** * @param propindex int index of the FO property * @return <tt>boolean</tt> is property inherited? * @throws PropertyException. */ public boolean isInherited(int propindex) throws PropertyException { Property property = setupProperty(propindex); return inherited[propindex] != Property.NO; } /** * @param property String name of the FO property * @return <tt>boolean</tt> is property inherited? * @throws PropertyException. */ public boolean isInherited(String property) throws PropertyException { return isInherited(PropNames.getPropertyIndex(property)); } /** * @param propindex int index of the FO property * @return <tt>boolean</tt> is property a shorthand? * @throws PropertyException. */ public boolean isShorthand(int propindex) throws PropertyException { Property property = setupProperty(propindex); return (datatypes[propindex] & Property.SHORTHAND) != 0; } /** * @param property String name of the FO property * @return <tt>boolean</tt> is property a shorthand? * @throws PropertyException. */ public boolean isShorthand(String property) throws PropertyException { return isShorthand(PropNames.getPropertyIndex(property)); } /** * @param propertyIndex int index of the FO property * @return <tt>boolean</tt> is property a compound? * @throws PropertyException. */ public boolean isCompound(int propertyIndex) throws PropertyException { Property property = setupProperty(propertyIndex); return (datatypes[propertyIndex] & Property.COMPOUND) != 0; } /** * @param property String name of the FO property * @return <tt>boolean</tt> is property a compound? * @throws PropertyException. */ public boolean isCompound(String property) throws PropertyException { return isCompound(PropNames.getPropertyIndex(property)); } /** * @param propertyIndex int index of the FO property * @return <tt>int</tt> dataTypes value. * @throws PropertyException. */ public int getDataTypes(int propertyIndex) throws PropertyException { Property property = setupProperty(propertyIndex); return datatypes[propertyIndex]; } /** * @param property String name of the FO property * @return <tt>int</tt> dataTypes value. * @throws PropertyException. */ public int getDataTypes(String property) throws PropertyException { return getDataTypes(PropNames.getPropertyIndex(property)); } /** * Map the integer value of an enum into its mapped value. * Only valid when the datatype of the property includes MAPPED_ENUM. * <p>Generally, the path will be enumText->enumIndex->mappedEnumText. * @param index <tt>int</tt> containing the enumeration index. * @param enumMap an <tt>ROStringArray</tt> of the <tt>String</tt>s * with the mapped enumeration values. * @return a <tt>String</tt> with the mapped enumeration text. */ public String enumIndexToMapping(int index, ROStringArray enumMap) { return enumMap.get(index); } /** * @param propindex <tt>int</tt> property index. * @param enum <tt>String</tt> containing the enumeration text. * @return <tt>int</tt> constant representing the enumeration value. * @exception PropertyException */ public int getEnumIndex(int propindex, String enum) throws PropertyException { Property property = setupProperty(propindex); return property.getEnumIndex(enum); } /** * @param propindex <tt>int</tt> property index. * @param enumIndex <tt>int</tt> containing the enumeration index. * @return <tt>String</tt> containing the enumeration text. * @exception PropertyException */ public String getEnumText(int propindex, int enumIndex) throws PropertyException { Property property = setupProperty(propindex); return property.getEnumText(enumIndex); } /** * Set up the details of a single property and return the * <tt>Property</tt> object. If the <tt>Property</tt> object * corresponding to the property index has not been resolved before, * derive the Class and Property objects, and extract certain field * values from the Property. * @param propindex - the <tt>int</tt> index. * @return - the <tt>Property</tt> corresponding to the index. * @throws <tt>PropertyException. */ public Property setupProperty(int propindex) throws PropertyException { String cname = ""; Class pclass; Property property; //System.out.println("setupProperty " + propindex + " " //+ PropNames.getPropertyName(propindex)); if ((property = properties[propindex]) != null) return property; // Get the property class name StringTokenizer stoke; stoke = new StringTokenizer (PropNames.getPropertyName(propindex), "-.:"); while (stoke.hasMoreTokens()) { String token = stoke.nextToken(); String pname = new Character( Character.toUpperCase(token.charAt(0)) ).toString() + token.substring(1); cname = cname + pname; } classNames[propindex] = cname; // Set up the classToIndex Hashmap with the name of the // property class as a key, and the integer index as a value if (classToIndex.put(cname, Ints.consts.get(propindex)) != null) throw new PropertyException ("Duplicate values in classToIndex for key " + cname); // Get the class for this property name String name = packageName + ".properties." + cname; try { //System.out.println("classes["+propindex+"] "+name);//DEBUG pclass = Class.forName(name); classes[propindex] = pclass; // Instantiate the class property = (Property)(pclass.newInstance()); properties[propindex] = property; //System.out.println //("property name " //+ property.getClass().getName()); //System.out.println //("property name " + //properties[propindex].getClass().getName()); // Set inheritance value if ((inherited[propindex] = pclass.getField("inherited").getInt(null)) != Property.NO) inheritedprops.set(propindex); // Set datatypes datatypes[propindex] = pclass.getField("dataTypes").getInt(null); //System.out.println("datatypes " + datatypes[propindex] + "\n" //+ Property.listDataTypes(datatypes[propindex])); // Set initialValueTypes initialValueTypes[propindex] = pclass.getField("initialValueType").getInt(null); //System.out.println("initialValueType " //+ initialValueTypes[propindex]); traitMappings[propindex] = pclass.getField("traitMapping").getInt(null); } catch (ClassNotFoundException e) { throw new PropertyException ("ClassNotFoundException" + e.getMessage()); } catch (IllegalAccessException e) { throw new PropertyException ("IllegalAccessException" + e.getMessage()); } catch (InstantiationException e) { throw new PropertyException ("InstantiationException" + e.getMessage()); } catch (NoSuchFieldException e) { throw new PropertyException ("NoSuchFieldException" + e.getMessage()); } return property; } private PropertyConsts () throws PropertyException {} }