You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Marker.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.fo.flow;
  19. import java.util.Collections;
  20. import java.util.Map;
  21. import org.xml.sax.Attributes;
  22. import org.xml.sax.Locator;
  23. import org.apache.fop.apps.FOPException;
  24. import org.apache.fop.fo.FONode;
  25. import org.apache.fop.fo.FOTreeBuilderContext;
  26. import org.apache.fop.fo.FObj;
  27. import org.apache.fop.fo.FObjMixed;
  28. import org.apache.fop.fo.PropertyList;
  29. import org.apache.fop.fo.PropertyListMaker;
  30. import org.apache.fop.fo.ValidationException;
  31. import org.apache.fop.fo.properties.Property;
  32. /**
  33. * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_marker">
  34. * <code>fo:marker<code></a> object.
  35. */
  36. public class Marker extends FObjMixed {
  37. // The value of properties relevant for fo:marker.
  38. private String markerClassName;
  39. // End of property values
  40. private PropertyListMaker savePropertyListMaker;
  41. private Map descendantPropertyLists = new java.util.HashMap();
  42. /**
  43. * Create a marker fo.
  44. *
  45. * @param parent the parent {@link FONode}
  46. */
  47. public Marker(FONode parent) {
  48. super(parent);
  49. }
  50. /** {@inheritDoc} */
  51. public void bind(PropertyList pList) throws FOPException {
  52. if (findAncestor(FO_FLOW) < 0) {
  53. invalidChildError(locator, getParent().getName(), FO_URI, getName(),
  54. "rule.markerDescendantOfFlow");
  55. }
  56. markerClassName = pList.get(PR_MARKER_CLASS_NAME).getString();
  57. if (markerClassName == null || markerClassName.equals("")) {
  58. missingPropertyError("marker-class-name");
  59. }
  60. }
  61. /**
  62. * Retrieve the property list of the given {@link FONode}
  63. * descendant
  64. *
  65. * @param foNode the {@link FONode} whose property list is requested
  66. * @return the {@link MarkerPropertyList} for the given node
  67. */
  68. protected MarkerPropertyList getPropertyListFor(FONode foNode) {
  69. return (MarkerPropertyList)
  70. descendantPropertyLists.get(foNode);
  71. }
  72. /** {@inheritDoc} */
  73. protected void startOfNode() {
  74. FOTreeBuilderContext builderContext = getBuilderContext();
  75. // Push a new property list maker which will make MarkerPropertyLists.
  76. savePropertyListMaker = builderContext.getPropertyListMaker();
  77. builderContext.setPropertyListMaker(new PropertyListMaker() {
  78. public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
  79. PropertyList pList = new MarkerPropertyList(fobj, parentPropertyList);
  80. descendantPropertyLists.put(fobj, pList);
  81. return pList;
  82. }
  83. });
  84. }
  85. /** {@inheritDoc} */
  86. protected void endOfNode() throws FOPException {
  87. super.endOfNode();
  88. // Pop the MarkerPropertyList maker.
  89. getBuilderContext().setPropertyListMaker(savePropertyListMaker);
  90. savePropertyListMaker = null;
  91. }
  92. /**
  93. * {@inheritDoc}
  94. * <br>XSL Content Model: (#PCDATA|%inline;|%block;)*
  95. * <br><i>Additionally: "An fo:marker may contain any formatting objects that
  96. * are permitted as a replacement of any fo:retrieve-marker that retrieves
  97. * the fo:marker's children."</i>
  98. * @todo implement "additional" constraint, possibly within fo:retrieve-marker
  99. */
  100. protected void validateChildNode(Locator loc, String nsURI, String localName)
  101. throws ValidationException {
  102. if (FO_URI.equals(nsURI)) {
  103. if (!isBlockOrInlineItem(nsURI, localName)) {
  104. invalidChildError(loc, nsURI, localName);
  105. }
  106. }
  107. }
  108. /** {@inheritDoc} */
  109. protected boolean inMarker() {
  110. return true;
  111. }
  112. /** @return the "marker-class-name" property */
  113. public String getMarkerClassName() {
  114. return markerClassName;
  115. }
  116. /** {@inheritDoc} */
  117. public String getLocalName() {
  118. return "marker";
  119. }
  120. /**
  121. * {@inheritDoc}
  122. * @return {@link org.apache.fop.fo.Constants#FO_MARKER}
  123. */
  124. public int getNameId() {
  125. return FO_MARKER;
  126. }
  127. /** {@inheritDoc} */
  128. public String toString() {
  129. StringBuffer sb = new StringBuffer(super.toString());
  130. sb.append(" {").append(getMarkerClassName()).append("}");
  131. return sb.toString();
  132. }
  133. /**
  134. * An implementation of {@link PropertyList} which only stores the explicitly
  135. * specified properties/attributes as bundles of name-value-namespace
  136. * strings
  137. */
  138. protected class MarkerPropertyList extends PropertyList
  139. implements Attributes {
  140. /** the array of attributes **/
  141. private MarkerAttribute[] attribs;
  142. /**
  143. * Overriding default constructor
  144. *
  145. * @param fobj the {@link FObj} to attach
  146. * @param parentPropertyList ignored
  147. */
  148. public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) {
  149. /* ignore parentPropertyList
  150. * won't be used because the attributes will be stored
  151. * without resolving
  152. */
  153. super(fobj, null);
  154. }
  155. /**
  156. * Override that doesn't convert the attributes to {@link Property}
  157. * instances, but simply stores the attributes for later processing.
  158. *
  159. * {@inheritDoc}
  160. */
  161. public void addAttributesToList(Attributes attributes)
  162. throws ValidationException {
  163. this.attribs = new MarkerAttribute[attributes.getLength()];
  164. String name;
  165. String value;
  166. String namespace;
  167. String qname;
  168. for (int i = attributes.getLength(); --i >= 0;) {
  169. namespace = attributes.getURI(i);
  170. qname = attributes.getQName(i);
  171. name = attributes.getLocalName(i);
  172. value = attributes.getValue(i);
  173. this.attribs[i] =
  174. MarkerAttribute.getInstance(namespace, qname, name, value);
  175. }
  176. }
  177. /** Null implementation; not used by this type of {@link PropertyList} */
  178. public void putExplicit(int propId, Property value) {
  179. //nop
  180. }
  181. /** Null implementation; not used by this type of {@link PropertyList} */
  182. public Property getExplicit(int propId) {
  183. return null;
  184. }
  185. /** {@inheritDoc} */
  186. public int getLength() {
  187. if (attribs == null) {
  188. return 0;
  189. } else {
  190. return attribs.length;
  191. }
  192. }
  193. /** {@inheritDoc} */
  194. public String getURI(int index) {
  195. if (attribs != null
  196. && index < attribs.length
  197. && index >= 0
  198. && attribs[index] != null) {
  199. return attribs[index].namespace;
  200. } else {
  201. return null;
  202. }
  203. }
  204. /** {@inheritDoc} */
  205. public String getLocalName(int index) {
  206. if (attribs != null
  207. && index < attribs.length
  208. && index >= 0
  209. && attribs[index] != null) {
  210. return attribs[index].name;
  211. } else {
  212. return null;
  213. }
  214. }
  215. /** {@inheritDoc} */
  216. public String getQName(int index) {
  217. if (attribs != null
  218. && index < attribs.length
  219. && index >= 0
  220. && attribs[index] != null) {
  221. return attribs[index].qname;
  222. } else {
  223. return null;
  224. }
  225. }
  226. /** Default implementation; not used */
  227. public String getType(int index) {
  228. return "CDATA";
  229. }
  230. /** {@inheritDoc} */
  231. public String getValue(int index) {
  232. if (attribs != null
  233. && index < attribs.length
  234. && index >= 0
  235. && attribs[index] != null) {
  236. return attribs[index].value;
  237. } else {
  238. return null;
  239. }
  240. }
  241. /** {@inheritDoc} */
  242. public int getIndex(String name, String namespace) {
  243. int index = -1;
  244. if (attribs != null && name != null && namespace != null) {
  245. for (int i = attribs.length; --i >= 0;) {
  246. if (attribs[i] != null
  247. && namespace.equals(attribs[i].namespace)
  248. && name.equals(attribs[i].name)) {
  249. break;
  250. }
  251. }
  252. }
  253. return index;
  254. }
  255. /** {@inheritDoc} */
  256. public int getIndex(String qname) {
  257. int index = -1;
  258. if (attribs != null && qname != null) {
  259. for (int i = attribs.length; --i >= 0;) {
  260. if (attribs[i] != null
  261. && qname.equals(attribs[i].qname)) {
  262. break;
  263. }
  264. }
  265. }
  266. return index;
  267. }
  268. /** Default implementation; not used */
  269. public String getType(String name, String namespace) {
  270. return "CDATA";
  271. }
  272. /** Default implementation; not used */
  273. public String getType(String qname) {
  274. return "CDATA";
  275. }
  276. /** {@inheritDoc} */
  277. public String getValue(String name, String namespace) {
  278. int index = getIndex(name, namespace);
  279. if (index > 0) {
  280. return getValue(index);
  281. }
  282. return null;
  283. }
  284. /** {@inheritDoc} */
  285. public String getValue(String qname) {
  286. int index = getIndex(qname);
  287. if (index > 0) {
  288. return getValue(index);
  289. }
  290. return null;
  291. }
  292. }
  293. /** Convenience inner class */
  294. private static final class MarkerAttribute {
  295. private static Map attributeCache =
  296. Collections.synchronizedMap(new java.util.WeakHashMap());
  297. protected String namespace;
  298. protected String qname;
  299. protected String name;
  300. protected String value;
  301. /**
  302. * Main constructor
  303. * @param namespace the namespace URI
  304. * @param qname the qualified name
  305. * @param name the name
  306. * @param value the value
  307. */
  308. private MarkerAttribute(String namespace, String qname,
  309. String name, String value) {
  310. this.namespace = namespace;
  311. this.qname = qname;
  312. this.name = (name == null ? qname : name);
  313. this.value = value;
  314. }
  315. /**
  316. * Convenience method, reduces the number
  317. * of distinct MarkerAttribute instances
  318. *
  319. * @param namespace the attribute namespace
  320. * @param qname the fully qualified name of the attribute
  321. * @param name the attribute name
  322. * @param value the attribute value
  323. * @return the single MarkerAttribute instance corresponding to
  324. * the name/value-pair
  325. */
  326. private static MarkerAttribute getInstance(
  327. String namespace, String qname,
  328. String name, String value) {
  329. MarkerAttribute newInstance =
  330. new MarkerAttribute(namespace, qname, name, value);
  331. if (attributeCache.containsKey(newInstance)) {
  332. return (MarkerAttribute) attributeCache.get(newInstance);
  333. } else {
  334. attributeCache.put(newInstance, newInstance);
  335. return newInstance;
  336. }
  337. }
  338. /** {@inheritDoc} */
  339. public boolean equals(Object o) {
  340. if (o instanceof MarkerAttribute) {
  341. MarkerAttribute attr = (MarkerAttribute) o;
  342. return ((attr.namespace == this.namespace)
  343. || (attr.namespace != null
  344. && attr.namespace.equals(this.namespace)))
  345. && ((attr.qname == this.qname)
  346. || (attr.qname != null
  347. && attr.qname.equals(this.qname)))
  348. && ((attr.name == this.name)
  349. || (attr.name != null
  350. && attr.name.equals(this.name)))
  351. && ((attr.value == this.value)
  352. || (attr.value != null
  353. && attr.value.equals(this.value)));
  354. } else {
  355. return false;
  356. }
  357. }
  358. }
  359. }