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.

PropertyList.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.fo;
  18. // Java
  19. import java.util.HashMap;
  20. import org.xml.sax.Attributes;
  21. // FOP
  22. import org.apache.fop.apps.FOPException;
  23. import org.apache.fop.fo.properties.Property;
  24. import org.apache.fop.fo.properties.PropertyMaker;
  25. /**
  26. * Class containing the collection of properties for a given FObj.
  27. */
  28. public class PropertyList extends HashMap {
  29. // writing-mode values
  30. private byte[] wmtable = null;
  31. // writing-mode index
  32. private int writingMode;
  33. private static boolean[] inheritableProperty;
  34. // absolute directions and dimensions
  35. /** constant for direction "left" */
  36. public static final int LEFT = 0;
  37. /** constant for direction "right" */
  38. public static final int RIGHT = 1;
  39. /** constant for direction "top" */
  40. public static final int TOP = 2;
  41. /** constant for direction "bottom" */
  42. public static final int BOTTOM = 3;
  43. /** constant for dimension "height" */
  44. public static final int HEIGHT = 4;
  45. /** constant for dimension "width" */
  46. public static final int WIDTH = 5;
  47. // directions relative to writing-mode
  48. /** constant for direction "start" */
  49. public static final int START = 0;
  50. /** constant for direction "end" */
  51. public static final int END = 1;
  52. /** constant for direction "before" */
  53. public static final int BEFORE = 2;
  54. /** constant for direction "after" */
  55. public static final int AFTER = 3;
  56. /** constant for dimension "block-progression-dimension" */
  57. public static final int BLOCKPROGDIM = 4;
  58. /** constant for dimension "inline-progression-dimension" */
  59. public static final int INLINEPROGDIM = 5;
  60. private static final String[] ABS_NAMES = new String[] {
  61. "left", "right", "top", "bottom", "height", "width"
  62. };
  63. private static final String[] REL_NAMES = new String[] {
  64. "start", "end", "before", "after", "block-progression-dimension",
  65. "inline-progression-dimension"
  66. };
  67. private static final HashMap WRITING_MODE_TABLES = new HashMap(4);
  68. {
  69. WRITING_MODE_TABLES.put(new Integer(Constants.WritingMode.LR_TB), /* lr-tb */
  70. new byte[] {
  71. START, END, BEFORE, AFTER, BLOCKPROGDIM, INLINEPROGDIM
  72. });
  73. WRITING_MODE_TABLES.put(new Integer(Constants.WritingMode.RL_TB), /* rl-tb */
  74. new byte[] {
  75. END, START, BEFORE, AFTER, BLOCKPROGDIM, INLINEPROGDIM
  76. });
  77. WRITING_MODE_TABLES.put(new Integer(Constants.WritingMode.TB_RL), /* tb-rl */
  78. new byte[] {
  79. AFTER, BEFORE, START, END, INLINEPROGDIM, BLOCKPROGDIM
  80. });
  81. }
  82. private PropertyList parentPropertyList = null;
  83. private String namespace = "";
  84. private String elementName = "";
  85. private FObj fobj = null;
  86. /**
  87. * Basic constructor.
  88. * @param parentPropertyList the PropertyList belonging to the new objects
  89. * parent
  90. * @param space name of namespace
  91. * @param elementName name of element
  92. */
  93. public PropertyList(FObj fObjToAttach, PropertyList parentPropertyList,
  94. String space, String elementName) {
  95. this.fobj = fObjToAttach;
  96. this.parentPropertyList = parentPropertyList;
  97. this.namespace = space;
  98. this.elementName = elementName;
  99. }
  100. /**
  101. * @return the FObj object to which this propertyList is attached
  102. */
  103. public FObj getFObj() {
  104. return this.fobj;
  105. }
  106. /**
  107. * @return the FObj object attached to the parentPropetyList
  108. */
  109. public FObj getParentFObj() {
  110. if (parentPropertyList != null) {
  111. return parentPropertyList.getFObj();
  112. } else {
  113. return null;
  114. }
  115. }
  116. /**
  117. * @return the FObj object attached to the parentPropetyList
  118. */
  119. public PropertyList getParentPropertyList() {
  120. return parentPropertyList;
  121. }
  122. /**
  123. * @return the namespace of this element
  124. */
  125. public String getNameSpace() {
  126. return namespace;
  127. }
  128. /**
  129. * @return element name for this
  130. */
  131. public String getElement() {
  132. return elementName;
  133. }
  134. /**
  135. * Return the value explicitly specified on this FO.
  136. * @param propertyName The name of the property whose value is desired.
  137. * It may be a compound name, such as space-before.optimum.
  138. * @return The value if the property is explicitly set or set by
  139. * a shorthand property, otherwise null.
  140. */
  141. public Property getExplicitOrShorthand(int propId) {
  142. /* Handle request for one part of a compound property */
  143. Property p = getExplicitBaseProp(propId & Constants.PROPERTY_MASK);
  144. if (p == null) {
  145. p = getShorthand(propId & Constants.PROPERTY_MASK);
  146. }
  147. if (p != null && (propId & Constants.COMPOUND_MASK) != 0) {
  148. return getSubpropValue(p, propId);
  149. }
  150. return p;
  151. }
  152. /**
  153. * Return the value explicitly specified on this FO.
  154. * @param propertyName The name of the property whose value is desired.
  155. * It may be a compound name, such as space-before.optimum.
  156. * @return The value if the property is explicitly set, otherwise null.
  157. */
  158. public Property getExplicit(int propId) {
  159. String propertyName = FOPropertyMapping.getPropertyName(propId);
  160. /* Handle request for one part of a compound property */
  161. if ((propId & Constants.COMPOUND_MASK) != 0) {
  162. Property p = getExplicitBaseProp(propId & Constants.PROPERTY_MASK);
  163. if (p != null) {
  164. return getSubpropValue(p, propId);
  165. } else {
  166. return null;
  167. }
  168. }
  169. return (Property) super.get(propertyName);
  170. }
  171. /**
  172. * Return the value explicitly specified on this FO.
  173. * @param propertyName The name of the base property whose value is desired.
  174. * @return The value if the property is explicitly set, otherwise null.
  175. */
  176. public Property getExplicitBaseProp(int propId) {
  177. String propertyName = FOPropertyMapping.getPropertyName(propId);
  178. return (Property) super.get(propertyName);
  179. }
  180. /**
  181. * Return the value of this property inherited by this FO.
  182. * Implements the inherited-property-value function.
  183. * The property must be inheritable!
  184. * @param propID The ID of the property whose value is desired.
  185. * @return The inherited value, otherwise null.
  186. */
  187. public Property getInherited(int propId) {
  188. if (parentPropertyList != null
  189. && isInherited(propId)) {
  190. return parentPropertyList.get(propId);
  191. } else {
  192. // return the "initial" value
  193. try {
  194. return makeProperty(propId);
  195. } catch (org.apache.fop.apps.FOPException e) {
  196. //log.error("Exception in getInherited(): property="
  197. // + propertyName + " : " + e);
  198. }
  199. }
  200. return null; // Exception in makeProperty!
  201. }
  202. /**
  203. * Return the property on the current FlowObject. If it isn't set explicitly,
  204. * this will try to compute it based on other properties, or if it is
  205. * inheritable, to return the inherited value. If all else fails, it returns
  206. * the default value.
  207. * @param propId The Constants ID of the property whose value is desired.
  208. * @return the Property corresponding to that name
  209. */
  210. public Property get(int propId) {
  211. return get(propId, true, true);
  212. }
  213. /**
  214. * Return the property on the current FlowObject. Depending on the passed flags,
  215. * this will try to compute it based on other properties, or if it is
  216. * inheritable, to return the inherited value. If all else fails, it returns
  217. * the default value.
  218. */
  219. private Property get(int propId, boolean bTryInherit,
  220. boolean bTryDefault) {
  221. PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
  222. try {
  223. return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
  224. bTryInherit, bTryDefault);
  225. } catch (FOPException exc) {
  226. fobj.getLogger().error("Error during property processing", exc);
  227. }
  228. return null;
  229. }
  230. /**
  231. * Return the "nearest" specified value for the given property.
  232. * Implements the from-nearest-specified-value function.
  233. * @param propertyName The name of the property whose value is desired.
  234. * @return The computed value if the property is explicitly set on some
  235. * ancestor of the current FO, else the initial value.
  236. */
  237. public Property getNearestSpecified(int propId) {
  238. Property p = null;
  239. for (PropertyList plist = this; p == null && plist != null;
  240. plist = plist.parentPropertyList) {
  241. p = plist.getExplicit(propId);
  242. }
  243. if (p == null) {
  244. // If no explicit setting found, return initial (default) value.
  245. try {
  246. p = makeProperty(propId);
  247. } catch (FOPException e) {
  248. //log.error("Exception in getNearestSpecified(): property="
  249. // + propertyName + " : " + e);
  250. }
  251. }
  252. return p;
  253. }
  254. /**
  255. * Return the value of this property on the parent of this FO.
  256. * Implements the from-parent function.
  257. * @param propId The Constants ID of the property whose value is desired.
  258. * @return The computed value on the parent or the initial value if this
  259. * FO is the root or is in a different namespace from its parent.
  260. */
  261. public Property getFromParent(int propId) {
  262. if (parentPropertyList != null) {
  263. return parentPropertyList.get(propId);
  264. } else {
  265. try {
  266. return makeProperty(propId);
  267. } catch (org.apache.fop.apps.FOPException e) {
  268. //log.error("Exception in getFromParent(): property="
  269. // + propertyName + " : " + e);
  270. }
  271. }
  272. return null; // Exception in makeProperty!
  273. }
  274. /**
  275. * Uses the stored writingMode.
  276. * @param absdir an absolute direction (top, bottom, left, right)
  277. * @return the corresponding writing model relative direction name
  278. * for the flow object.
  279. */
  280. public int wmMap(int lrtb, int rltb, int tbrl) {
  281. switch (writingMode) {
  282. case Constants.WritingMode.LR_TB: return lrtb;
  283. case Constants.WritingMode.RL_TB: return rltb;
  284. case Constants.WritingMode.TB_RL: return tbrl;
  285. }
  286. return -1;
  287. }
  288. /**
  289. * Uses the stored writingMode.
  290. * @param reldir a writing mode relative direction (start, end, before, after)
  291. * @return the corresponding absolute direction name for the flow object.
  292. */
  293. public String wmRelToAbs(int reldir) {
  294. if (wmtable != null) {
  295. for (int i = 0; i < wmtable.length; i++) {
  296. if (wmtable[i] == reldir) {
  297. return ABS_NAMES[i];
  298. }
  299. }
  300. }
  301. return "";
  302. }
  303. /**
  304. * Set the writing mode traits for the FO with this property list.
  305. * @param writingMode the writing-mode property to be set for this object
  306. */
  307. public void setWritingMode(int writingMode) {
  308. this.writingMode = writingMode;
  309. this.wmtable = (byte[])WRITING_MODE_TABLES.get(new Integer(writingMode));
  310. }
  311. /**
  312. *
  313. * @param attributes Collection of attributes passed to us from the parser.
  314. * @throws FOPException If an error occurs while building the PropertyList
  315. */
  316. public void addAttributesToList(Attributes attributes)
  317. throws FOPException {
  318. /*
  319. * If font-size is set on this FO, must set it first, since
  320. * other attributes specified in terms of "ems" depend on it.
  321. */
  322. /** @todo When we do "shorthand" properties, must handle the
  323. * "font" property as well to see if font-size is set.
  324. */
  325. String attributeName = "font-size";
  326. String attributeValue = attributes.getValue(attributeName);
  327. convertAttributeToProperty(attributes, attributeName,
  328. attributeValue);
  329. for (int i = 0; i < attributes.getLength(); i++) {
  330. attributeName = attributes.getQName(i);
  331. attributeValue = attributes.getValue(i);
  332. convertAttributeToProperty(attributes, attributeName,
  333. attributeValue);
  334. }
  335. }
  336. /**
  337. *
  338. * @param attributes Collection of attributes
  339. * @param attributeName Attribute name to convert
  340. * @param attributeValue Attribute value to assign to property
  341. */
  342. private void convertAttributeToProperty(Attributes attributes,
  343. String attributeName,
  344. String attributeValue) {
  345. PropertyMaker propertyMaker = null;
  346. FObj parentFO = fobj.findNearestAncestorFObj();
  347. /* Handle "compound" properties, ex. space-before.minimum */
  348. String basePropertyName = findBasePropertyName(attributeName);
  349. String subPropertyName = findSubPropertyName(attributeName);
  350. int propId = FOPropertyMapping.getPropertyId(basePropertyName);
  351. propertyMaker = findMaker(propId);
  352. if (propertyMaker == null) {
  353. handleInvalidProperty(attributeName);
  354. return;
  355. }
  356. if (attributeValue == null) {
  357. return;
  358. }
  359. try {
  360. Property prop = null;
  361. if (subPropertyName == null) { // base attribute only found
  362. /* Do nothing if the base property has already been created.
  363. * This is e.g. the case when a compound attribute was
  364. * specified before the base attribute; in these cases
  365. * the base attribute was already created in
  366. * findBaseProperty()
  367. */
  368. if (super.get(basePropertyName) != null) {
  369. return;
  370. }
  371. prop = propertyMaker.make(this, attributeValue, parentFO);
  372. } else { // e.g. "leader-length.maximum"
  373. Property baseProperty = findBaseProperty(attributes,
  374. parentFO, basePropertyName, propertyMaker);
  375. int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
  376. prop = propertyMaker.make(baseProperty, subpropId,
  377. this, attributeValue, parentFO);
  378. }
  379. if (prop != null) {
  380. put(basePropertyName, prop);
  381. }
  382. } catch (FOPException e) {
  383. /**@todo log this exception */
  384. // log.error(e.getMessage());
  385. }
  386. }
  387. private Property findBaseProperty(Attributes attributes,
  388. FObj parentFO,
  389. String basePropName,
  390. PropertyMaker propertyMaker)
  391. throws FOPException {
  392. /* If the baseProperty has already been created, return it
  393. * e.g. <fo:leader xxxx="120pt" xxxx.maximum="200pt"... />
  394. */
  395. Property baseProperty = (Property) super.get(basePropName);
  396. if (baseProperty != null) {
  397. return baseProperty;
  398. }
  399. /* Otherwise If it is specified later in this list of Attributes, create it now
  400. * e.g. <fo:leader xxxx.maximum="200pt" xxxx="200pt"... />
  401. */
  402. String basePropertyValue = attributes.getValue(basePropName);
  403. if (basePropertyValue != null && propertyMaker != null) {
  404. baseProperty = propertyMaker.make(this, basePropertyValue,
  405. parentFO);
  406. return baseProperty;
  407. }
  408. return null; // could not find base property
  409. }
  410. private void handleInvalidProperty(String attributeName) {
  411. if (!attributeName.startsWith("xmlns")) {
  412. //log.error("property '"
  413. // + attributeName + "' ignored");
  414. }
  415. }
  416. /**
  417. * Finds the first or base part (up to any period) of an attribute name.
  418. * For example, if input is "space-before.minimum", should return
  419. * "space-before".
  420. * @param attributeName String to be atomized
  421. * @return the base portion of the attribute
  422. */
  423. private static String findBasePropertyName(String attributeName) {
  424. int sepCharIndex = attributeName.indexOf('.');
  425. String basePropName = attributeName;
  426. if (sepCharIndex > -1) {
  427. basePropName = attributeName.substring(0, sepCharIndex);
  428. }
  429. return basePropName;
  430. }
  431. /**
  432. * Finds the second or sub part (portion past any period) of an attribute
  433. * name. For example, if input is "space-before.minimum", should return
  434. * "minimum".
  435. * @param attributeName String to be atomized
  436. * @return the sub portion of the attribute
  437. */
  438. private static String findSubPropertyName(String attributeName) {
  439. int sepCharIndex = attributeName.indexOf('.');
  440. String subPropName = null;
  441. if (sepCharIndex > -1) {
  442. subPropName = attributeName.substring(sepCharIndex + 1);
  443. }
  444. return subPropName;
  445. }
  446. /**
  447. * @param propId ID of property
  448. * @param p a Property object
  449. * @return the sub-property
  450. */
  451. private Property getSubpropValue(Property p, int propId) {
  452. PropertyMaker maker = findMaker(propId & Constants.PROPERTY_MASK);
  453. if (maker != null) {
  454. return maker.getSubprop(p, propId & Constants.COMPOUND_MASK);
  455. } else {
  456. return null;
  457. }
  458. }
  459. /**
  460. * @param propId ID of property
  461. * @return new Property object
  462. */
  463. private Property getShorthand(int propId) {
  464. PropertyMaker propertyMaker = findMaker(propId);
  465. if (propertyMaker != null) {
  466. return propertyMaker.getShorthand(this);
  467. } else {
  468. //log.error("no Maker for " + propertyName);
  469. return null;
  470. }
  471. }
  472. /**
  473. * @param propID ID of property
  474. * @return new Property object
  475. * @throws FOPException for errors in the input
  476. */
  477. private Property makeProperty(int propId) throws FOPException {
  478. Property p = null;
  479. PropertyMaker propertyMaker = findMaker(propId);
  480. if (propertyMaker != null) {
  481. p = propertyMaker.make(this);
  482. } else {
  483. //log.error("property " + propertyName
  484. // + " ignored");
  485. }
  486. return p;
  487. }
  488. /**
  489. * @param propId ID of property
  490. * @return isInherited value from the requested Property.Maker
  491. */
  492. private boolean isInherited(int propId) {
  493. if (inheritableProperty == null) {
  494. inheritableProperty = new boolean[Constants.PROPERTY_COUNT + 1];
  495. PropertyMaker maker = null;
  496. for (int prop = 1; prop <= Constants.PROPERTY_COUNT; prop++) {
  497. maker = findMaker(prop);
  498. inheritableProperty[prop] = (maker != null && maker.isInherited());
  499. }
  500. }
  501. return inheritableProperty[propId];
  502. }
  503. /**
  504. * @param propId Id of property
  505. * @return the Property.Maker for this property
  506. */
  507. private PropertyMaker findMaker(int propId) {
  508. if (propId < 1 || propId > Constants.PROPERTY_COUNT) {
  509. return null;
  510. } else {
  511. return FObj.propertyListTable[propId];
  512. }
  513. }
  514. }