package org.apache.fop.fo; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.PropertyConsts; import org.apache.fop.fo.FObjectNames; import org.apache.fop.datatypes.Ints; import org.apache.fop.messaging.MessageHandler; import org.apache.fop.xml.FoXMLEvent; import org.apache.fop.xml.XMLNamespaces; import org.xml.sax.Attributes; import java.util.Iterator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Collections; import java.util.Arrays; /* * FOAttributes.java * $Id$ * * Created: Wed Nov 14 15:19:51 2001 * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. * * @author <a href="mailto:pbwest@powerup.com.au">Peter B. West</a> * @version $Revision$ $Name$ */ /** * The FO Attributes data structures and methods needed to manage the * Attributes associated with FO nodes. */ public class FOAttributes { private static final String tag = "$Name$"; private static final String revision = "$Revision$"; /** * <i>nSpaceAttrMaps</i> is an <tt>ArrayList</tt> to hold the array of * <tt>HashMap</tt>s which contain the attribute lists for each * namespace which may be active for a particular FO element. The * <tt>ArrayList</tt> is indexed by the URIIndex for this namespace * which is maintained in an <tt>XMLNamespaces</tt> object by the * <tt>FOTree</tt> object which is processing the FO input. The * values in the <tt>HashMap</tt>s are indexed by the local name of the * attribute. * The <tt>ArrayList</tt> will not be created for a particular instance * of <tt>FOAttributes</tt> unless a namespace other than the standard * XSL namespace is activated for this instance. * See <i>foAttrMap</i>. */ private ArrayList nSpaceAttrMaps; /** * <i>foAttrMap</i> is a <tt>HashMap</tt> to hold the FO namespace * attribute list specified in the FO element with which this list is * associated. The <tt>String</tt> attribute value is stored * indexed by the integer constant property identifier from * <tt>PropertyConsts</tt>. */ private HashMap foAttrMap = new HashMap(0); /** * A sorted array of the keys (property indices) of the values in * <i>foAttrMap</i>. */ private Integer[] foAttrKeys = null; /** * A static array of <tt>Integer</tt> as a template for the generation * of the <i>foAttrKeys</i> array. */ private static Integer[] integerArray = new Integer[] { Ints.consts.get(0) }; /** * Construct an <i>FOAttributes</i> object. * <p>The <tt>Attributes</tt> object on the event is scanned, and each * attribute is examined. If the attribute is in the default namespace * for fo: attributes, it is an fo: property, and its value is entered * into the <i>foAttrMap</i> <tt>Hashmap</tt> indexed by the property * index. * <p>If the attribute does not belong to the default namespace, its * value is entered into the appropriate <tt>HashMap</tt> in the * <tt>ArrayList</tt> <i>nSpaceAttrMaps</i>, indexed by the attribute's * local name. * @param event - the FO XML event which triggered construction of the * parent <tt>FONode</tt>. * @param foNode - the <tt>FONode</tt> with which these attributes are * associated. */ public FOAttributes(FoXMLEvent event, FONode foNode) throws FOPException { // If the event is null, there is no event associated with this // node, probably because this is a manufactured node; e.g., // an "invented" FopageSequenceMaster. The default initialisation // includes an empty foAttrMap HashMap. if (event == null) return; if (event.getFoType() == FObjectNames.PCDATA) return; // go with the empty foAttrMap // Create the foAttrMap. Attributes attributes = event.getAttributes(); if (attributes == null) throw new FOPException ("No Attributes in XMLEvent"); int propIndex; HashMap tmpHash; for (int i = 0; i < attributes.getLength(); i++) { String attrUri = attributes.getURI(i); String attrLocalname = attributes.getLocalName(i); String attrQName = attributes.getQName(i); int sep = attrQName.indexOf(':'); String prefix = attrQName.substring(0, (sep == -1 ? 0 : sep)); if (prefix.equals("xmlns")) break; String attrValue = attributes.getValue(i); int attrUriIndex = foNode.namespaces.getURIIndex(attrUri); //System.out.println("FONode:" + event); if (attrUriIndex == XMLNamespaces.DefAttrNSIndex) { // Standard FO namespace // Catch default namespace declaration here. if (attrLocalname.equals("xmlns")) break; // Is this a known (valid) property? propIndex = PropNames.getPropertyIndex(attrLocalname); // Known attribute name foAttrMap.put(Ints.consts.get(propIndex), attrValue); } else { // Not the XSL FO namespace int j; if (nSpaceAttrMaps == null) { //Create the list System.out.println("Creating nSpaceAttrMaps"); nSpaceAttrMaps = new ArrayList(attrUriIndex + 1); // Add the fo list for (j = 0; j < XMLNamespaces.DefAttrNSIndex; j++) nSpaceAttrMaps.add(new HashMap(0)); System.out.println("Adding foAttrMap"); nSpaceAttrMaps.add(foAttrMap); } // Make sure there are elements between the last current // and the one to be added for (j = nSpaceAttrMaps.size(); j <= attrUriIndex; j++) nSpaceAttrMaps.add(new HashMap(0)); // Does a HashMap exist for this namespace? if ((tmpHash = (HashMap)nSpaceAttrMaps.get(attrUriIndex)) == null) { tmpHash = new HashMap(1); nSpaceAttrMaps.set(attrUriIndex, tmpHash); } // Now put this value in the HashMap tmpHash.put(attrLocalname, attrValue); } } // Set up the sorted array of the foAttr keys, if foAttrMap has // any entries. if (foAttrMap.size() > 0) { foAttrKeys = (Integer[])(foAttrMap.keySet().toArray(integerArray)); Arrays.sort(foAttrKeys); } // Finished with the Attributes object event.setAttributes(null); } /** * Get the default namespace attribute values as an unmodifiable * <tt>Map</tt>. * @return a unmodifiable <tt>Map</tt> containing the the attribute * values for all of the default attribute namespace attributes in this * attribute list, indexed by the property name index from * <tt>PropNames</tt>. */ public Map getFixedFoAttrMap() { return Collections.unmodifiableMap((Map)foAttrMap); } /** * Get the <tt>HashMap</tt> of all default namespace attributes. * @return <tt>HashMap</tt> <i>foAttrMap</i> containing the the attribute * values for all of the default attribute namespace attributes in this * attribute list, indexed by the property name index from * <tt>PropNames</tt>. This HashMap may be changed by the calling * process. */ public HashMap getFoAttrMap() { return foAttrMap; } /** * Get the sorted array of property index keys for the default namespace * attributes. * @return <tt>Integer[]</tt> <i>foAttrKeys</i> containing the the * sorted keys (the property indices from <tt>PropNames</tt>) of the * attribute values for all of the default attribute namespace attributes * in this attribute list. * Warning: This array may be changed by the calling process. */ public Integer[] getFoAttrKeys() { return foAttrKeys; } /** * A convenience method for accessing attribute values from the default * attribute namespace. * @param property an <tt>int</tt> containing the property name index * from <tt>PropNames</tt>. * @return a <tt>String</tt> containing the associated property value. */ public String getFoAttrValue(int property) { return (String)(foAttrMap.get(Ints.consts.get(property))); } /** * A convenience method for accessing attribute values from the default * attribute namespace. * @param propertyName a <tt>String</tt> containing the property name. * @return a <tt>String</tt> containing the associated property value. */ public String getFoAttrValue(String propertyName) throws PropertyException { return getFoAttrValue(PropNames.getPropertyIndex(propertyName)); } /** * Get an unmodifiable <tt>Map</tt> of the attribute values for a * particular namespace. * @param uriIndex an <tt>int</tt> containing the index of the attribute * values namespace, maintained in an <tt>XMLEvent</tt> <tt>static</tt> * array. * @return an unmodifiable <tt>Map</tt> of the attribute values * within the indexed namespace, for this attribute list, indexed by the * local name of the attribute. The <tt>Map</tt> returned is * derived from the one maintained in <i>nSpaceAttrMaps</i>. */ public Map getAttrMap(int uriIndex) { if (uriIndex == XMLNamespaces.DefAttrNSIndex) return Collections.unmodifiableMap((Map)foAttrMap); if (nSpaceAttrMaps != null) { if (uriIndex >= nSpaceAttrMaps.size()) return null; return Collections.unmodifiableMap ((Map)(nSpaceAttrMaps.get(uriIndex))); } else { return null; } } /** * Get the value of an attribute in a particular namespace. * @param uriIndex an <tt>int</tt> index of the URIs maintained * by <tt>XMLEvent</tt>. * @param localName a <tt>String</tt> with the local name of the * attribute. In the case of the default attribute namespace, this * will be the fo property name. * @return a <tt>String</tt> containing the value of the attribute. */ public String getUriAttrValue(int uriIndex, String localName) throws PropertyException { if (uriIndex == XMLNamespaces.DefAttrNSIndex) return getFoAttrValue(PropNames.getPropertyIndex(localName)); return (String) (((HashMap)nSpaceAttrMaps.get(uriIndex)).get(localName)); } /** * Get the size of the <i>foAttrMap</i> <tt>HashMap</tt> * containing attributes for the default fo: namespace * @return an <tt>int</tt> containing the size. */ public int getFoAttrMapSize() { return foAttrMap.size(); } /** * Get the size of the <i>nSpaceAttrMaps</i> <tt>ArrayList</tt> * containing attribute namespaces active in this set of attributes. * <i>N.B.</i> this may be zero if only the default attribute * namespace has been seen in the attribute set. * @return an <tt>int</tt> containing the size. */ public int getNSpaceAttrMapsSize() { if (nSpaceAttrMaps == null) return 0; return nSpaceAttrMaps.size(); } /** * Merge attributes from another <tt>FOAttributes</tt> into <i>this</i>. * @param foAttrs the <tt>FOAttributes</tt> containing the attributes * to merge. */ public void merge(FOAttributes foAttrs) { foAttrMap.putAll(foAttrs.getFoAttrMap()); int attrLen = foAttrs.getNSpaceAttrMapsSize(); if (attrLen != 0) { // something to copy if (nSpaceAttrMaps == null) { // no "foreign" attribute lists in this // copy the others in nSpaceAttrMaps = new ArrayList(attrLen); } // If the merging ArrayList of namespaces is larger, add the // extra elements and initialise each to an empty HashMap for (int i = nSpaceAttrMaps.size(); i < attrLen; i++) nSpaceAttrMaps.add(new HashMap(0)); // Except for foAttrs, which has already been merged, merge // the entries from the merging foAttrs for (int i = 0; i < attrLen; i++) { // skip foAttrMap if (i == XMLNamespaces.DefAttrNSIndex) continue; ((HashMap) nSpaceAttrMaps.get(i)) .putAll(foAttrs.getAttrMap(i)); } } } }// FOAttributes