001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: Marker.java 489885 2006-12-23 11:57:29Z adelmelle $ */
019:
020: package org.apache.fop.fo.flow;
021:
022: import java.util.Collections;
023: import java.util.List;
024: import java.util.Map;
025:
026: import org.xml.sax.Attributes;
027: import org.xml.sax.Locator;
028:
029: import org.apache.fop.apps.FOPException;
030: import org.apache.fop.fo.FOEventHandler;
031: import org.apache.fop.fo.FONode;
032: import org.apache.fop.fo.FObj;
033: import org.apache.fop.fo.FObjMixed;
034: import org.apache.fop.fo.PropertyList;
035: import org.apache.fop.fo.PropertyListMaker;
036: import org.apache.fop.fo.ValidationException;
037: import org.apache.fop.fo.properties.Property;
038:
039: /**
040: * Marker formatting object.
041: */
042: public class Marker extends FObjMixed {
043: // The value of properties relevant for fo:marker.
044: private String markerClassName;
045: // End of property values
046:
047: private PropertyListMaker savePropertyListMaker;
048: private Map descendantPropertyLists = new java.util.HashMap();
049:
050: /**
051: * Create a marker fo.
052: * @param parent the parent fo node
053: */
054: public Marker(FONode parent) {
055: super (parent);
056: }
057:
058: /**
059: * @see org.apache.fop.fo.FObj#bind(PropertyList)
060: */
061: public void bind(PropertyList pList) throws FOPException {
062: if (findAncestor(FO_FLOW) < 0) {
063: invalidChildError(locator, FO_URI, "marker",
064: "An fo:marker is permitted only as the descendant "
065: + "of an fo:flow");
066: }
067:
068: markerClassName = pList.get(PR_MARKER_CLASS_NAME).getString();
069:
070: if (markerClassName == null || markerClassName.equals("")) {
071: missingPropertyError("marker-class-name");
072: }
073: }
074:
075: /**
076: * retrieve the property list of foNode
077: * @param foNode the FO node whose property list is requested
078: * @return the MarkerPropertyList of foNode
079: */
080: protected MarkerPropertyList getPropertyListFor(FONode foNode) {
081: return (MarkerPropertyList) descendantPropertyLists.get(foNode);
082: }
083:
084: /** @see org.apache.fop.fo.FONode#startOfNode() */
085: protected void startOfNode() {
086: FOEventHandler foEventHandler = getFOEventHandler();
087: // Push a new property list maker which will make MarkerPropertyLists.
088: savePropertyListMaker = foEventHandler.getPropertyListMaker();
089: foEventHandler.setPropertyListMaker(new PropertyListMaker() {
090: public PropertyList make(FObj fobj,
091: PropertyList parentPropertyList) {
092: PropertyList pList = new MarkerPropertyList(fobj,
093: parentPropertyList);
094: descendantPropertyLists.put(fobj, pList);
095: return pList;
096: }
097: });
098: }
099:
100: /** @see org.apache.fop.fo.FONode#endOfNode() */
101: protected void endOfNode() throws FOPException {
102: super .endOfNode();
103: // Pop the MarkerPropertyList maker.
104: getFOEventHandler().setPropertyListMaker(savePropertyListMaker);
105: savePropertyListMaker = null;
106: }
107:
108: /**
109: * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
110: * XSL Content Model: (#PCDATA|%inline;|%block;)*
111: * Additionally: "An fo:marker may contain any formatting objects that
112: * are permitted as a replacement of any fo:retrieve-marker that retrieves
113: * the fo:marker's children."
114: * @todo implement "additional" constraint, possibly within fo:retrieve-marker
115: */
116: protected void validateChildNode(Locator loc, String nsURI,
117: String localName) throws ValidationException {
118: if (!isBlockOrInlineItem(nsURI, localName)) {
119: invalidChildError(loc, nsURI, localName);
120: }
121: }
122:
123: protected boolean inMarker() {
124: return true;
125: }
126:
127: /**
128: * Return the "marker-class-name" property.
129: */
130: public String getMarkerClassName() {
131: return markerClassName;
132: }
133:
134: /** @see org.apache.fop.fo.FONode#getLocalName() */
135: public String getLocalName() {
136: return "marker";
137: }
138:
139: /**
140: * @see org.apache.fop.fo.FObj#getNameId()
141: */
142: public int getNameId() {
143: return FO_MARKER;
144: }
145:
146: /** @see java.lang.Object#toString() */
147: public String toString() {
148: StringBuffer sb = new StringBuffer(super .toString());
149: sb.append(" {").append(getMarkerClassName()).append("}");
150: return sb.toString();
151: }
152:
153: /**
154: * An implementation of PropertyList which only stores the explicitly
155: * specified properties/attributes as bundles of name-value-namespace
156: * strings
157: */
158: protected class MarkerPropertyList extends PropertyList implements
159: Attributes {
160:
161: /** the array of attributes **/
162: private MarkerAttribute[] attribs;
163:
164: /**
165: * Overriding default constructor
166: *
167: * @param fobj the FObj to attach
168: * @param parentPropertyList ignored
169: */
170: public MarkerPropertyList(FObj fobj,
171: PropertyList parentPropertyList) {
172: /* ignore parentPropertyList
173: * won't be used because the attributes will be stored
174: * without resolving
175: */
176: super (fobj, null);
177: }
178:
179: /**
180: * Override that doesn't convert the attributes to Property instances,
181: * but simply stores the attributes for later processing;
182: *
183: * @see org.apache.fop.fo.PropertyList#addAttributesToList(Attributes)
184: */
185: public void addAttributesToList(Attributes attributes)
186: throws ValidationException {
187:
188: this .attribs = new MarkerAttribute[attributes.getLength()];
189:
190: String name;
191: String value;
192: String namespace;
193: String qname;
194:
195: for (int i = attributes.getLength(); --i >= 0;) {
196: namespace = attributes.getURI(i);
197: qname = attributes.getQName(i);
198: name = attributes.getLocalName(i);
199: value = attributes.getValue(i);
200:
201: this .attribs[i] = MarkerAttribute.getInstance(
202: namespace, qname, name, value);
203: }
204: }
205:
206: /**
207: * Null implementation; not used by this type of PropertyList
208: * @see org.apache.fop.fo.PropertyList#putExplicit(int, Property)
209: */
210: public void putExplicit(int propId, Property value) {
211: //nop
212: }
213:
214: /**
215: * Null implementation; not used by this type of PropertyList
216: * @see org.apache.fop.fo.PropertyList#getExplicit(int)
217: */
218: public Property getExplicit(int propId) {
219: return null;
220: }
221:
222: /**
223: * @see org.xml.sax.Attributes#getLength()
224: */
225: public int getLength() {
226: if (attribs == null) {
227: return 0;
228: } else {
229: return attribs.length;
230: }
231: }
232:
233: /**
234: * @see org.xml.sax.Attributes#getURI(int)
235: */
236: public String getURI(int index) {
237: if (attribs != null && index < attribs.length && index >= 0
238: && attribs[index] != null) {
239: return attribs[index].namespace;
240: } else {
241: return null;
242: }
243: }
244:
245: /**
246: * @see org.xml.sax.Attributes#getLocalName(int)
247: */
248: public String getLocalName(int index) {
249: if (attribs != null && index < attribs.length && index >= 0
250: && attribs[index] != null) {
251: return attribs[index].name;
252: } else {
253: return null;
254: }
255: }
256:
257: /**
258: * @see org.xml.sax.Attributes#getQName(int)
259: */
260: public String getQName(int index) {
261: if (attribs != null && index < attribs.length && index >= 0
262: && attribs[index] != null) {
263: return attribs[index].qname;
264: } else {
265: return null;
266: }
267: }
268:
269: /**
270: * Default implementation; not used
271: * @see org.xml.sax.Attributes#getType(int)
272: */
273: public String getType(int index) {
274: return "CDATA";
275: }
276:
277: /**
278: * @see org.xml.sax.Attributes#getValue(int)
279: */
280: public String getValue(int index) {
281: if (attribs != null && index < attribs.length && index >= 0
282: && attribs[index] != null) {
283: return attribs[index].value;
284: } else {
285: return null;
286: }
287: }
288:
289: /**
290: * @see org.xml.sax.Attributes#getIndex(String, String)
291: */
292: public int getIndex(String name, String namespace) {
293: int index = -1;
294: if (attribs != null && name != null && namespace != null) {
295: for (int i = attribs.length; --i >= 0;) {
296: if (attribs[i] != null
297: && namespace.equals(attribs[i].namespace)
298: && name.equals(attribs[i].name)) {
299: break;
300: }
301: }
302: }
303: return index;
304: }
305:
306: /**
307: * @see org.xml.sax.Attributes#getIndex(String)
308: */
309: public int getIndex(String qname) {
310: int index = -1;
311: if (attribs != null && qname != null) {
312: for (int i = attribs.length; --i >= 0;) {
313: if (attribs[i] != null
314: && qname.equals(attribs[i].qname)) {
315: break;
316: }
317: }
318: }
319: return index;
320: }
321:
322: /**
323: * Default implementation; not used
324: * @see org.xml.sax.Attributes#getType(String, String)
325: */
326: public String getType(String name, String namespace) {
327: return "CDATA";
328: }
329:
330: /**
331: * Default implementation; not used
332: * @see org.xml.sax.Attributes#getType(String)
333: */
334: public String getType(String qname) {
335: return "CDATA";
336: }
337:
338: /**
339: * @see org.xml.sax.Attributes#getValue(String, String)
340: */
341: public String getValue(String name, String namespace) {
342: int index = getIndex(name, namespace);
343: if (index > 0) {
344: return getValue(index);
345: }
346: return null;
347: }
348:
349: /**
350: * @see org.xml.sax.Attributes#getValue(String)
351: */
352: public String getValue(String qname) {
353: int index = getIndex(qname);
354: if (index > 0) {
355: return getValue(index);
356: }
357: return null;
358: }
359: }
360:
361: /**
362: * Convenience inner class
363: */
364: private static final class MarkerAttribute {
365:
366: private static Map attributeCache = Collections
367: .synchronizedMap(new java.util.WeakHashMap());
368:
369: protected String namespace;
370: protected String qname;
371: protected String name;
372: protected String value;
373:
374: /**
375: * Main constructor
376: * @param namespace the namespace URI
377: * @param qname the qualified name
378: * @param name the name
379: * @param value the value
380: */
381: private MarkerAttribute(String namespace, String qname,
382: String name, String value) {
383: this .namespace = namespace;
384: this .qname = qname;
385: this .name = (name == null ? qname : name);
386: this .value = value;
387: }
388:
389: /**
390: * Convenience method, reduces the number
391: * of distinct MarkerAttribute instances
392: *
393: * @param name the attribute name
394: * @param value the attribute value
395: * @return the single MarkerAttribute instance corresponding to
396: * the name/value-pair
397: */
398: private static MarkerAttribute getInstance(String namespace,
399: String qname, String name, String value) {
400: MarkerAttribute newInstance = new MarkerAttribute(
401: namespace, qname, name, value);
402: if (attributeCache.containsKey(newInstance)) {
403: return (MarkerAttribute) attributeCache
404: .get(newInstance);
405: } else {
406: attributeCache.put(newInstance, newInstance);
407: return newInstance;
408: }
409: }
410:
411: /**
412: * @see java.lang.Object#equals(Object)
413: */
414: public boolean equals(Object o) {
415: if (o instanceof MarkerAttribute) {
416: MarkerAttribute attr = (MarkerAttribute) o;
417: return ((attr.namespace == this .namespace) || (attr.namespace != null && attr.namespace
418: .equals(this .namespace)))
419: && ((attr.qname == this .qname) || (attr.qname != null && attr.qname
420: .equals(this .qname)))
421: && ((attr.name == this .name) || (attr.name != null && attr.name
422: .equals(this .name)))
423: && ((attr.value == this .value) || (attr.value != null && attr.value
424: .equals(this .value)));
425: } else {
426: return false;
427: }
428: }
429: }
430: }
|