Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

PropertyList.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  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;
  19. // Java
  20. import org.xml.sax.Attributes;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.xmlgraphics.util.QName;
  24. import org.apache.fop.apps.FopFactory;
  25. import org.apache.fop.fo.expr.PropertyException;
  26. import org.apache.fop.fo.properties.CommonAbsolutePosition;
  27. import org.apache.fop.fo.properties.CommonAccessibility;
  28. import org.apache.fop.fo.properties.CommonAural;
  29. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  30. import org.apache.fop.fo.properties.CommonFont;
  31. import org.apache.fop.fo.properties.CommonHyphenation;
  32. import org.apache.fop.fo.properties.CommonMarginBlock;
  33. import org.apache.fop.fo.properties.CommonMarginInline;
  34. import org.apache.fop.fo.properties.CommonRelativePosition;
  35. import org.apache.fop.fo.properties.CommonTextDecoration;
  36. import org.apache.fop.fo.properties.Property;
  37. import org.apache.fop.fo.properties.PropertyMaker;
  38. /**
  39. * Class containing the collection of properties for a given FObj.
  40. */
  41. public abstract class PropertyList {
  42. // writing-mode index
  43. private int writingMode;
  44. private static boolean[] inheritableProperty;
  45. /** reference to the parent FO's propertyList **/
  46. protected PropertyList parentPropertyList = null;
  47. private FObj fobj = null;
  48. private static Log log = LogFactory.getLog(PropertyList.class);
  49. /**
  50. * Basic constructor.
  51. * @param fObjToAttach the FO this PropertyList should be attached to
  52. * @param parentPropertyList the PropertyList belonging to the new objects
  53. * parent
  54. */
  55. public PropertyList(FObj fObjToAttach, PropertyList parentPropertyList) {
  56. this.fobj = fObjToAttach;
  57. this.parentPropertyList = parentPropertyList;
  58. }
  59. /**
  60. * @return the FObj object to which this propertyList is attached
  61. */
  62. public FObj getFObj() {
  63. return this.fobj;
  64. }
  65. /**
  66. * @return the FObj object attached to the parentPropertyList
  67. */
  68. public FObj getParentFObj() {
  69. if (parentPropertyList != null) {
  70. return parentPropertyList.getFObj();
  71. } else {
  72. return null;
  73. }
  74. }
  75. /**
  76. * @return the FObj object attached to the parentPropetyList
  77. */
  78. public PropertyList getParentPropertyList() {
  79. return parentPropertyList;
  80. }
  81. /**
  82. * Return the value explicitly specified on this FO.
  83. * @param propId The id of the property whose value is desired.
  84. * @return The value if the property is explicitly set or set by
  85. * a shorthand property, otherwise null.
  86. * @throws PropertyException ...
  87. */
  88. public Property getExplicitOrShorthand(int propId) throws PropertyException {
  89. /* Handle request for one part of a compound property */
  90. Property p = getExplicit(propId);
  91. if (p == null) {
  92. p = getShorthand(propId);
  93. }
  94. return p;
  95. }
  96. /**
  97. * Return the value explicitly specified on this FO.
  98. * @param propId The ID of the property whose value is desired.
  99. * @return The value if the property is explicitly set, otherwise null.
  100. */
  101. public abstract Property getExplicit(int propId);
  102. /**
  103. * Set an value defined explicitly on this FO.
  104. * @param propId The ID of the property to set.
  105. * @param value The value of the property.
  106. */
  107. public abstract void putExplicit(int propId, Property value);
  108. /**
  109. * Return the value of this property inherited by this FO.
  110. * Implements the inherited-property-value function.
  111. * The property must be inheritable!
  112. * @param propId The ID of the property whose value is desired.
  113. * @return The inherited value, otherwise null.
  114. * @throws PropertyException ...
  115. */
  116. public Property getInherited(int propId) throws PropertyException {
  117. if (isInherited(propId)) {
  118. return getFromParent(propId);
  119. } else {
  120. // return the "initial" value
  121. return makeProperty(propId);
  122. }
  123. }
  124. /**
  125. * Return the property on the current FlowObject. If it isn't set explicitly,
  126. * this will try to compute it based on other properties, or if it is
  127. * inheritable, to return the inherited value. If all else fails, it returns
  128. * the default value.
  129. * @param propId The Constants ID of the property whose value is desired.
  130. * @return the Property corresponding to that name
  131. * @throws PropertyException if there is a problem evaluating the property
  132. */
  133. public Property get(int propId) throws PropertyException {
  134. return get(propId, true, true);
  135. }
  136. /**
  137. * Return the property on the current FlowObject. Depending on the passed flags,
  138. * this will try to compute it based on other properties, or if it is
  139. * inheritable, to return the inherited value. If all else fails, it returns
  140. * the default value.
  141. * @param propId the property's id
  142. * @param bTryInherit true for inherited properties, or when the inherited
  143. * value is needed
  144. * @param bTryDefault true when the default value may be used as a last resort
  145. * @return the property
  146. * @throws PropertyException if there is a problem evaluating the property
  147. */
  148. public Property get(int propId, boolean bTryInherit,
  149. boolean bTryDefault) throws PropertyException {
  150. PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
  151. if (propertyMaker != null) {
  152. return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
  153. bTryInherit, bTryDefault);
  154. }
  155. return null;
  156. }
  157. /**
  158. * Return the "nearest" specified value for the given property.
  159. * Implements the from-nearest-specified-value function.
  160. * @param propId The ID of the property whose value is desired.
  161. * @return The computed value if the property is explicitly set on some
  162. * ancestor of the current FO, else the initial value.
  163. * @throws PropertyException if there an error occurred when getting the property
  164. */
  165. public Property getNearestSpecified(int propId) throws PropertyException {
  166. Property p = null;
  167. PropertyList pList = parentPropertyList;
  168. while (pList != null) {
  169. p = pList.getExplicit(propId);
  170. if (p != null) {
  171. return p;
  172. } else {
  173. pList = pList.parentPropertyList;
  174. }
  175. }
  176. // If no explicit value found on any of the ancestor-nodes,
  177. // return initial (default) value.
  178. return makeProperty(propId);
  179. }
  180. /**
  181. * Return the value of this property on the parent of this FO.
  182. * Implements the from-parent function.
  183. * @param propId The Constants ID of the property whose value is desired.
  184. * @return The computed value on the parent or the initial value if this
  185. * FO is the root or is in a different namespace from its parent.
  186. * @throws PropertyException ...
  187. */
  188. public Property getFromParent(int propId) throws PropertyException {
  189. if (parentPropertyList != null) {
  190. return parentPropertyList.get(propId);
  191. } else {
  192. return makeProperty(propId);
  193. }
  194. }
  195. /**
  196. * Set writing mode for this FO.
  197. * Use that from the nearest ancestor, including self, which generates
  198. * reference areas, or from root FO if no ancestor found.
  199. * @throws PropertyException ...
  200. */
  201. public void setWritingMode() throws PropertyException {
  202. FObj p = fobj.findNearestAncestorFObj();
  203. // If this is a reference area or the root, use the property value.
  204. if (fobj.generatesReferenceAreas() || p == null) {
  205. writingMode = get(Constants.PR_WRITING_MODE).getEnum();
  206. } else {
  207. // Otherwise get the writing mode value from the parent.
  208. writingMode = getParentPropertyList().getWritingMode();
  209. }
  210. }
  211. /**
  212. * Return the "writing-mode" property value.
  213. * @return the "writing-mode" property value.
  214. */
  215. public int getWritingMode() {
  216. return writingMode;
  217. }
  218. /**
  219. * Uses the stored writingMode.
  220. * @param lrtb the property ID to return under lrtb writingmode.
  221. * @param rltb the property ID to return under rltb writingmode.
  222. * @param tbrl the property ID to return under tbrl writingmode.
  223. * @return one of the property IDs, depending on the writing mode.
  224. */
  225. public int getWritingMode(int lrtb, int rltb, int tbrl) {
  226. switch (writingMode) {
  227. case Constants.EN_LR_TB: return lrtb;
  228. case Constants.EN_RL_TB: return rltb;
  229. case Constants.EN_TB_RL: return tbrl;
  230. default:
  231. //nop
  232. }
  233. return -1;
  234. }
  235. /**
  236. * Adds the attributes, passed in by the parser to the PropertyList
  237. *
  238. * @param attributes Collection of attributes passed to us from the parser.
  239. * @throws ValidationException if there is an attribute that does not
  240. * map to a property id (strict validation only)
  241. */
  242. public void addAttributesToList(Attributes attributes)
  243. throws ValidationException {
  244. /*
  245. * If column-number/number-columns-spanned are specified, then we
  246. * need them before all others (possible from-table-column() on any
  247. * other property further in the list...
  248. */
  249. String attributeName = "column-number";
  250. String attributeValue = attributes.getValue(attributeName);
  251. convertAttributeToProperty(attributes, attributeName,
  252. attributeValue);
  253. attributeName = "number-columns-spanned";
  254. attributeValue = attributes.getValue(attributeName);
  255. convertAttributeToProperty(attributes, attributeName,
  256. attributeValue);
  257. /*
  258. * If font-size is set on this FO, must set it first, since
  259. * other attributes specified in terms of "ems" depend on it.
  260. */
  261. attributeName = "font";
  262. attributeValue = attributes.getValue(attributeName);
  263. convertAttributeToProperty(attributes, attributeName,
  264. attributeValue);
  265. if (attributeValue == null) {
  266. /*
  267. * font shorthand wasn't specified, so still need to process
  268. * explicit font-size
  269. */
  270. attributeName = "font-size";
  271. attributeValue = attributes.getValue(attributeName);
  272. convertAttributeToProperty(attributes, attributeName,
  273. attributeValue);
  274. }
  275. String attributeNS;
  276. FopFactory factory = getFObj().getUserAgent().getFactory();
  277. for (int i = 0; i < attributes.getLength(); i++) {
  278. /* convert all attributes with the same namespace as the fo element
  279. * the "xml:lang" property is a special case */
  280. attributeNS = attributes.getURI(i);
  281. attributeName = attributes.getQName(i);
  282. attributeValue = attributes.getValue(i);
  283. if (attributeNS == null || attributeNS.length() == 0
  284. || "xml:lang".equals(attributeName)) {
  285. convertAttributeToProperty(attributes, attributeName, attributeValue);
  286. } else if (!factory.isNamespaceIgnored(attributeNS)) {
  287. ElementMapping mapping = factory.getElementMappingRegistry().getElementMapping(
  288. attributeNS);
  289. QName attr = new QName(attributeNS, attributeName);
  290. if (mapping != null) {
  291. if (mapping.isAttributeProperty(attr)
  292. && mapping.getStandardPrefix() != null) {
  293. convertAttributeToProperty(attributes,
  294. mapping.getStandardPrefix() + ":" + attr.getLocalName(),
  295. attributeValue);
  296. } else {
  297. getFObj().addForeignAttribute(attr, attributeValue);
  298. }
  299. } else {
  300. handleInvalidProperty(attr);
  301. }
  302. }
  303. }
  304. }
  305. /**
  306. * Validates a property name.
  307. * @param propertyName the property name to check
  308. * @return true if the base property name and the subproperty name (if any)
  309. * can be correctly mapped to an id
  310. */
  311. protected boolean isValidPropertyName(String propertyName) {
  312. int propId = FOPropertyMapping.getPropertyId(
  313. findBasePropertyName(propertyName));
  314. int subpropId = FOPropertyMapping.getSubPropertyId(
  315. findSubPropertyName(propertyName));
  316. return !(propId == -1
  317. || (subpropId == -1
  318. && findSubPropertyName(propertyName) != null));
  319. }
  320. /**
  321. *
  322. * @param attributes Collection of attributes
  323. * @param attributeName Attribute name to convert
  324. * @param attributeValue Attribute value to assign to property
  325. * @throws ValidationException in case the property name is invalid
  326. * for the FO namespace
  327. */
  328. private void convertAttributeToProperty(Attributes attributes,
  329. String attributeName,
  330. String attributeValue)
  331. throws ValidationException {
  332. if (attributeValue != null) {
  333. if (attributeName.startsWith("xmlns:")
  334. || "xmlns".equals(attributeName)) {
  335. //Ignore namespace declarations if the XML parser/XSLT processor
  336. //reports them as 'regular' attributes
  337. return;
  338. }
  339. /* Handle "compound" properties, ex. space-before.minimum */
  340. String basePropertyName = findBasePropertyName(attributeName);
  341. String subPropertyName = findSubPropertyName(attributeName);
  342. int propId = FOPropertyMapping.getPropertyId(basePropertyName);
  343. int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
  344. if (propId == -1
  345. || (subpropId == -1 && subPropertyName != null)) {
  346. handleInvalidProperty(new QName(null, attributeName));
  347. }
  348. FObj parentFO = fobj.findNearestAncestorFObj();
  349. PropertyMaker propertyMaker = findMaker(propId);
  350. if (propertyMaker == null) {
  351. log.warn("No PropertyMaker registered for " + attributeName
  352. + ". Ignoring property.");
  353. return;
  354. }
  355. try {
  356. Property prop = null;
  357. if (subPropertyName == null) { // base attribute only found
  358. /* Do nothing if the base property has already been created.
  359. * This is e.g. the case when a compound attribute was
  360. * specified before the base attribute; in these cases
  361. * the base attribute was already created in
  362. * findBaseProperty()
  363. */
  364. if (getExplicit(propId) != null) {
  365. return;
  366. }
  367. prop = propertyMaker.make(this, attributeValue, parentFO);
  368. } else { // e.g. "leader-length.maximum"
  369. Property baseProperty
  370. = findBaseProperty(attributes, parentFO, propId,
  371. basePropertyName, propertyMaker);
  372. prop = propertyMaker.make(baseProperty, subpropId,
  373. this, attributeValue, parentFO);
  374. }
  375. if (prop != null) {
  376. putExplicit(propId, prop);
  377. }
  378. } catch (PropertyException e) {
  379. fobj.getFOValidationEventProducer().invalidPropertyValue(this, fobj.getName(),
  380. attributeName, attributeValue, e, fobj.locator);
  381. }
  382. }
  383. }
  384. private Property findBaseProperty(Attributes attributes,
  385. FObj parentFO,
  386. int propId,
  387. String basePropertyName,
  388. PropertyMaker propertyMaker)
  389. throws PropertyException {
  390. /* If the baseProperty has already been created, return it
  391. * e.g. <fo:leader xxxx="120pt" xxxx.maximum="200pt"... />
  392. */
  393. Property baseProperty = getExplicit(propId);
  394. if (baseProperty != null) {
  395. return baseProperty;
  396. }
  397. /* Otherwise If it is specified later in this list of Attributes, create it now
  398. * e.g. <fo:leader xxxx.maximum="200pt" xxxx="200pt"... />
  399. */
  400. String basePropertyValue = attributes.getValue(basePropertyName);
  401. if (basePropertyValue != null && propertyMaker != null) {
  402. baseProperty = propertyMaker.make(this, basePropertyValue,
  403. parentFO);
  404. return baseProperty;
  405. }
  406. return null; // could not find base property
  407. }
  408. /**
  409. * Handles an invalid property.
  410. * @param attr the invalid attribute
  411. * @throws ValidationException if an exception needs to be thrown depending on the
  412. * validation settings
  413. */
  414. protected void handleInvalidProperty(QName attr)
  415. throws ValidationException {
  416. if (!attr.getQName().startsWith("xmlns")) {
  417. fobj.getFOValidationEventProducer().invalidProperty(this, fobj.getName(),
  418. attr, true, fobj.locator);
  419. }
  420. }
  421. /**
  422. * Finds the first or base part (up to any period) of an attribute name.
  423. * For example, if input is "space-before.minimum", should return
  424. * "space-before".
  425. * @param attributeName String to be atomized
  426. * @return the base portion of the attribute
  427. */
  428. protected static String findBasePropertyName(String attributeName) {
  429. int separatorCharIndex = attributeName.indexOf('.');
  430. String basePropertyName = attributeName;
  431. if (separatorCharIndex > -1) {
  432. basePropertyName = attributeName.substring(0, separatorCharIndex);
  433. }
  434. return basePropertyName;
  435. }
  436. /**
  437. * Finds the second or sub part (portion past any period) of an attribute
  438. * name. For example, if input is "space-before.minimum", should return
  439. * "minimum".
  440. * @param attributeName String to be atomized
  441. * @return the sub portion of the attribute
  442. */
  443. protected static String findSubPropertyName(String attributeName) {
  444. int separatorCharIndex = attributeName.indexOf('.');
  445. String subpropertyName = null;
  446. if (separatorCharIndex > -1) {
  447. subpropertyName = attributeName.substring(separatorCharIndex + 1);
  448. }
  449. return subpropertyName;
  450. }
  451. /**
  452. * @param propId ID of property
  453. * @return new Property object
  454. * @throws PropertyException if there's a problem while processing the property
  455. */
  456. private Property getShorthand(int propId) throws PropertyException {
  457. PropertyMaker propertyMaker = findMaker(propId);
  458. if (propertyMaker != null) {
  459. return propertyMaker.getShorthand(this);
  460. } else {
  461. //log.error("no Maker for " + propertyName);
  462. return null;
  463. }
  464. }
  465. /**
  466. * @param propId ID of property
  467. * @return new Property object
  468. * @throws PropertyException if there's a problem while processing the property
  469. */
  470. private Property makeProperty(int propId) throws PropertyException {
  471. PropertyMaker propertyMaker = findMaker(propId);
  472. if (propertyMaker != null) {
  473. return propertyMaker.make(this);
  474. } else {
  475. //log.error("property " + propertyName
  476. // + " ignored");
  477. }
  478. return null;
  479. }
  480. /**
  481. * @param propId ID of property
  482. * @return isInherited value from the requested Property.Maker
  483. */
  484. private boolean isInherited(int propId) {
  485. if (inheritableProperty == null) {
  486. inheritableProperty = new boolean[Constants.PROPERTY_COUNT + 1];
  487. PropertyMaker maker = null;
  488. for (int prop = 1; prop <= Constants.PROPERTY_COUNT; prop++) {
  489. maker = findMaker(prop);
  490. inheritableProperty[prop] = (maker != null && maker.isInherited());
  491. }
  492. }
  493. return inheritableProperty[propId];
  494. }
  495. /**
  496. * @param propId Id of property
  497. * @return the Property.Maker for this property
  498. */
  499. private PropertyMaker findMaker(int propId) {
  500. if (propId < 1 || propId > Constants.PROPERTY_COUNT) {
  501. return null;
  502. } else {
  503. return FObj.getPropertyMakerFor(propId);
  504. }
  505. }
  506. /**
  507. * Constructs a BorderAndPadding object.
  508. * @return a BorderAndPadding object
  509. * @throws PropertyException if there's a problem while processing the properties
  510. */
  511. public CommonBorderPaddingBackground getBorderPaddingBackgroundProps()
  512. throws PropertyException {
  513. return CommonBorderPaddingBackground.getInstance(this);
  514. }
  515. /**
  516. * Constructs a CommonHyphenation object.
  517. * @return the CommonHyphenation object
  518. * @throws PropertyException if there's a problem while processing the properties
  519. */
  520. public CommonHyphenation getHyphenationProps() throws PropertyException {
  521. return CommonHyphenation.getInstance(this);
  522. }
  523. /**
  524. * Constructs a CommonMarginBlock object.
  525. * @return the CommonMarginBlock object
  526. * @throws PropertyException if there's a problem while processing the properties
  527. */
  528. public CommonMarginBlock getMarginBlockProps() throws PropertyException {
  529. return new CommonMarginBlock(this);
  530. }
  531. /**
  532. * Constructs a CommonMarginInline object.
  533. * @return the CommonMarginInline object
  534. * @throws PropertyException if there's a problem while processing the properties
  535. */
  536. public CommonMarginInline getMarginInlineProps() throws PropertyException {
  537. return new CommonMarginInline(this);
  538. }
  539. /**
  540. * Constructs a CommonAccessibility object.
  541. * @return the CommonAccessibility object
  542. * @throws PropertyException if there's a problem while processing the properties
  543. */
  544. public CommonAccessibility getAccessibilityProps() throws PropertyException {
  545. return new CommonAccessibility(this);
  546. }
  547. /**
  548. * Constructs a CommonAural object.
  549. * @return the CommonAural object
  550. * @throws PropertyException if there's a problem while processing the properties
  551. */
  552. public CommonAural getAuralProps() throws PropertyException {
  553. CommonAural props = new CommonAural(this);
  554. return props;
  555. }
  556. /**
  557. * Constructs a RelativePositionProps objects.
  558. * @return a RelativePositionProps object
  559. * @throws PropertyException if there's a problem while processing the properties
  560. */
  561. public CommonRelativePosition getRelativePositionProps() throws PropertyException {
  562. return new CommonRelativePosition(this);
  563. }
  564. /**
  565. * Constructs a CommonAbsolutePosition object.
  566. * @return the CommonAbsolutePosition object
  567. * @throws PropertyException if there's a problem while processing the properties
  568. */
  569. public CommonAbsolutePosition getAbsolutePositionProps() throws PropertyException {
  570. return new CommonAbsolutePosition(this);
  571. }
  572. /**
  573. * Constructs a CommonFont object.
  574. *
  575. * @return A CommonFont object
  576. * @throws PropertyException if there's a problem while processing the properties
  577. */
  578. public CommonFont getFontProps() throws PropertyException {
  579. return CommonFont.getInstance(this);
  580. }
  581. /**
  582. * Constructs a CommonTextDecoration object.
  583. * @return a CommonTextDecoration object
  584. * @throws PropertyException if there's a problem while processing the properties
  585. */
  586. public CommonTextDecoration getTextDecorationProps() throws PropertyException {
  587. return CommonTextDecoration.createFromPropertyList(this);
  588. }
  589. }