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.

AbstractLayoutManager.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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.layoutmgr;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import java.util.ListIterator;
  22. import java.util.Map;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.apache.xmlgraphics.util.QName;
  26. import org.apache.fop.area.Area;
  27. import org.apache.fop.area.AreaTreeObject;
  28. import org.apache.fop.area.PageViewport;
  29. import org.apache.fop.fo.Constants;
  30. import org.apache.fop.fo.FONode;
  31. import org.apache.fop.fo.FObj;
  32. import org.apache.fop.fo.flow.Marker;
  33. import org.apache.fop.fo.flow.RetrieveMarker;
  34. /**
  35. * The base class for most LayoutManagers.
  36. */
  37. public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager implements Constants {
  38. /** logging instance */
  39. private static Log log = LogFactory.getLog(AbstractLayoutManager.class);
  40. /** Parent LayoutManager for this LayoutManager */
  41. protected LayoutManager parentLayoutManager;
  42. /** List of child LayoutManagers */
  43. protected List<LayoutManager> childLMs;
  44. /** Iterator for child LayoutManagers */
  45. protected ListIterator fobjIter;
  46. /** Marker map for markers related to this LayoutManager */
  47. private Map<String, Marker> markers;
  48. /** True if this LayoutManager has handled all of its content. */
  49. private boolean isFinished;
  50. /** child LM during getNextKnuthElement phase */
  51. protected LayoutManager curChildLM;
  52. /** child LM iterator during getNextKnuthElement phase */
  53. protected ListIterator<LayoutManager> childLMiter;
  54. private int lastGeneratedPosition = -1;
  55. private int smallestPosNumberChecked = Integer.MAX_VALUE;
  56. /**
  57. * Abstract layout manager.
  58. */
  59. public AbstractLayoutManager() {
  60. }
  61. /**
  62. * Abstract layout manager.
  63. *
  64. * @param fo the formatting object for this layout manager
  65. */
  66. public AbstractLayoutManager(FObj fo) {
  67. super(fo);
  68. if (fo == null) {
  69. throw new IllegalStateException("Null formatting object found.");
  70. }
  71. markers = fo.getMarkers();
  72. fobjIter = fo.getChildNodes();
  73. childLMiter = new LMiter(this);
  74. }
  75. /** {@inheritDoc} */
  76. public void setParent(LayoutManager lm) {
  77. this.parentLayoutManager = lm;
  78. }
  79. /** {@inheritDoc} */
  80. public LayoutManager getParent() {
  81. return this.parentLayoutManager;
  82. }
  83. /** {@inheritDoc} */
  84. public void initialize() {
  85. // Empty
  86. }
  87. /**
  88. * Return currently active child LayoutManager or null if
  89. * all children have finished layout.
  90. * Note: child must implement LayoutManager! If it doesn't, skip it
  91. * and print a warning.
  92. * @return the current child LayoutManager
  93. */
  94. protected LayoutManager getChildLM() {
  95. if (curChildLM != null && !curChildLM.isFinished()) {
  96. return curChildLM;
  97. }
  98. if (childLMiter.hasNext()) {
  99. curChildLM = childLMiter.next();
  100. curChildLM.initialize();
  101. return curChildLM;
  102. }
  103. return null;
  104. }
  105. /**
  106. * Set currently active child layout manager.
  107. * @param childLM the child layout manager
  108. */
  109. protected void setCurrentChildLM(LayoutManager childLM) {
  110. curChildLM = childLM;
  111. childLMiter = new LMiter(this);
  112. do {
  113. curChildLM = childLMiter.next();
  114. } while (curChildLM != childLM);
  115. }
  116. /**
  117. * Return indication if getChildLM will return another LM.
  118. * @return true if another child LM is still available
  119. */
  120. protected boolean hasNextChildLM() {
  121. return childLMiter.hasNext();
  122. }
  123. /**
  124. * Tell whether this LayoutManager has handled all of its content.
  125. * @return True if there are no more break possibilities,
  126. * ie. the last one returned represents the end of the content.
  127. */
  128. public boolean isFinished() {
  129. return isFinished;
  130. }
  131. /**
  132. * Set the flag indicating the LayoutManager has handled all of its content.
  133. * @param fin the flag value to be set
  134. */
  135. public void setFinished(boolean fin) {
  136. isFinished = fin;
  137. }
  138. /** {@inheritDoc} */
  139. public void addAreas(PositionIterator posIter, LayoutContext context) {
  140. }
  141. /** {@inheritDoc} */
  142. public List getNextKnuthElements(LayoutContext context, int alignment) {
  143. log.warn("null implementation of getNextKnuthElements() called!");
  144. setFinished(true);
  145. return null;
  146. }
  147. /** {@inheritDoc} */
  148. public List getChangedKnuthElements(List oldList, int alignment) {
  149. log.warn("null implementation of getChangeKnuthElement() called!");
  150. return null;
  151. }
  152. /**
  153. * Return an Area which can contain the passed childArea. The childArea
  154. * may not yet have any content, but it has essential traits set.
  155. * In general, if the LayoutManager already has an Area it simply returns
  156. * it. Otherwise, it makes a new Area of the appropriate class.
  157. * It gets a parent area for its area by calling its parent LM.
  158. * Finally, based on the dimensions of the parent area, it initializes
  159. * its own area. This includes setting the content IPD and the maximum
  160. * BPD.
  161. * @param childArea the child area for which the parent area is wanted
  162. * @return the parent area for the given child
  163. */
  164. public Area getParentArea(Area childArea) {
  165. return null;
  166. }
  167. /**
  168. * Add a child area to the current area. If this causes the maximum
  169. * dimension of the current area to be exceeded, the parent LM is called
  170. * to add it.
  171. * @param childArea the child area to be added
  172. */
  173. public void addChildArea(Area childArea) {
  174. }
  175. /**
  176. * Create the LM instances for the children of the
  177. * formatting object being handled by this LM.
  178. * @param size the requested number of child LMs
  179. * @return the list with the preloaded child LMs
  180. */
  181. protected List<LayoutManager> createChildLMs(int size) {
  182. if (fobjIter == null) {
  183. return null;
  184. }
  185. List<LayoutManager> newLMs = new ArrayList<LayoutManager>(size);
  186. while (fobjIter.hasNext() && newLMs.size() < size ) {
  187. Object theobj = fobjIter.next();
  188. if (theobj instanceof FONode) {
  189. FONode foNode = (FONode) theobj;
  190. if (foNode instanceof RetrieveMarker) {
  191. foNode = getPSLM().resolveRetrieveMarker(
  192. (RetrieveMarker) foNode);
  193. }
  194. if (foNode != null) {
  195. getPSLM().getLayoutManagerMaker()
  196. .makeLayoutManagers(foNode, newLMs);
  197. }
  198. }
  199. }
  200. return newLMs;
  201. }
  202. /** {@inheritDoc} */
  203. public PageSequenceLayoutManager getPSLM() {
  204. return parentLayoutManager.getPSLM();
  205. }
  206. /**
  207. * @see PageSequenceLayoutManager#getCurrentPage()
  208. * @return the {@link Page} instance corresponding to the current page
  209. */
  210. public Page getCurrentPage() {
  211. return getPSLM().getCurrentPage();
  212. }
  213. /** @return the current page viewport */
  214. public PageViewport getCurrentPV() {
  215. return getPSLM().getCurrentPage().getPageViewport();
  216. }
  217. /** {@inheritDoc} */
  218. public boolean createNextChildLMs(int pos) {
  219. List<LayoutManager> newLMs = createChildLMs(pos + 1 - childLMs.size());
  220. addChildLMs(newLMs);
  221. return pos < childLMs.size();
  222. }
  223. /** {@inheritDoc} */
  224. public List<LayoutManager> getChildLMs() {
  225. if (childLMs == null) {
  226. childLMs = new java.util.ArrayList<LayoutManager>(10);
  227. }
  228. return childLMs;
  229. }
  230. /** {@inheritDoc} */
  231. public void addChildLM(LayoutManager lm) {
  232. if (lm == null) {
  233. return;
  234. }
  235. lm.setParent(this);
  236. if (childLMs == null) {
  237. childLMs = new java.util.ArrayList<LayoutManager>(10);
  238. }
  239. childLMs.add(lm);
  240. if (log.isTraceEnabled()) {
  241. log.trace(this.getClass().getName()
  242. + ": Adding child LM " + lm.getClass().getName());
  243. }
  244. }
  245. /** {@inheritDoc} */
  246. public void addChildLMs(List newLMs) {
  247. if (newLMs == null || newLMs.size() == 0) {
  248. return;
  249. }
  250. ListIterator<LayoutManager> iter = newLMs.listIterator();
  251. while (iter.hasNext()) {
  252. addChildLM(iter.next());
  253. }
  254. }
  255. /**
  256. * Adds a Position to the Position participating in the first|last determination by assigning
  257. * it a unique position index.
  258. * @param pos the Position
  259. * @return the same Position but with a position index
  260. */
  261. public Position notifyPos(Position pos) {
  262. if (pos.getIndex() >= 0) {
  263. throw new IllegalStateException("Position already got its index");
  264. }
  265. pos.setIndex(++lastGeneratedPosition);
  266. return pos;
  267. }
  268. private void verifyNonNullPosition(Position pos) {
  269. if (pos == null || pos.getIndex() < 0) {
  270. throw new IllegalArgumentException(
  271. "Only non-null Positions with an index can be checked");
  272. }
  273. }
  274. /**
  275. * Indicates whether the given Position is the first area-generating Position of this LM.
  276. * @param pos the Position (must be one with a position index)
  277. * @return True if it is the first Position
  278. */
  279. public boolean isFirst(Position pos) {
  280. //log.trace("isFirst() smallestPosNumberChecked=" + smallestPosNumberChecked + " " + pos);
  281. verifyNonNullPosition(pos);
  282. if (pos.getIndex() == this.smallestPosNumberChecked) {
  283. return true;
  284. } else if (pos.getIndex() < this.smallestPosNumberChecked) {
  285. this.smallestPosNumberChecked = pos.getIndex();
  286. return true;
  287. } else {
  288. return false;
  289. }
  290. }
  291. /**
  292. * Indicates whether the given Position is the last area-generating Position of this LM.
  293. * @param pos the Position (must be one with a position index)
  294. * @return True if it is the last Position
  295. */
  296. public boolean isLast(Position pos) {
  297. verifyNonNullPosition(pos);
  298. return (pos.getIndex() == this.lastGeneratedPosition
  299. && isFinished());
  300. }
  301. /**
  302. * Transfers foreign attributes from the formatting object to the area.
  303. * @param targetArea the area to set the attributes on
  304. */
  305. protected void transferForeignAttributes(AreaTreeObject targetArea) {
  306. Map<QName, String> atts = fobj.getForeignAttributes();
  307. targetArea.setForeignAttributes(atts);
  308. }
  309. /**
  310. * Transfers extension attachments from the formatting object to the area.
  311. * @param targetArea the area to set the extensions on
  312. */
  313. protected void transferExtensionAttachments(AreaTreeObject targetArea) {
  314. if (fobj.hasExtensionAttachments()) {
  315. targetArea.setExtensionAttachments(fobj.getExtensionAttachments());
  316. }
  317. }
  318. /**
  319. * Transfers extensions (foreign attributes and extension attachments) from
  320. * the formatting object to the area.
  321. * @param targetArea the area to set the extensions on
  322. */
  323. protected void transferExtensions(AreaTreeObject targetArea) {
  324. transferForeignAttributes(targetArea);
  325. transferExtensionAttachments(targetArea);
  326. }
  327. /**
  328. * Registers the FO's markers on the current PageViewport
  329. *
  330. * @param isStarting boolean indicating whether the markers qualify as 'starting'
  331. * @param isFirst boolean indicating whether the markers qualify as 'first'
  332. * @param isLast boolean indicating whether the markers qualify as 'last'
  333. */
  334. protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) {
  335. if (this.markers != null) {
  336. getCurrentPV().addMarkers(
  337. this.markers,
  338. isStarting,
  339. isFirst,
  340. isLast);
  341. }
  342. }
  343. /**
  344. * Registers the FO's id on the current PageViewport
  345. */
  346. protected void addId() {
  347. if (fobj != null) {
  348. getPSLM().addIDToPage(fobj.getId());
  349. }
  350. }
  351. /**
  352. * Notifies the {@link PageSequenceLayoutManager} that layout
  353. * for this LM has ended.
  354. */
  355. protected void notifyEndOfLayout() {
  356. if (fobj != null) {
  357. getPSLM().notifyEndOfLayout(fobj.getId());
  358. }
  359. }
  360. /**
  361. * Checks to see if the incoming {@link Position}
  362. * is the last one for this LM, and if so, calls
  363. * {@link #notifyEndOfLayout()} and cleans up.
  364. *
  365. * @param pos the {@link Position} to check
  366. */
  367. protected void checkEndOfLayout(Position pos) {
  368. if (pos != null
  369. && pos.getLM() == this
  370. && this.isLast(pos)) {
  371. notifyEndOfLayout();
  372. /* References to the child LMs are no longer needed
  373. */
  374. childLMs = null;
  375. curChildLM = null;
  376. childLMiter = null;
  377. /* markers that qualify have been transferred to the page
  378. */
  379. markers = null;
  380. /* References to the FO's children can be released if the
  381. * LM is a descendant of the FlowLM. For static-content
  382. * the FO may still be needed on following pages.
  383. */
  384. LayoutManager lm = this.parentLayoutManager;
  385. while (!(lm instanceof FlowLayoutManager
  386. || lm instanceof PageSequenceLayoutManager)) {
  387. lm = lm.getParent();
  388. }
  389. if (lm instanceof FlowLayoutManager) {
  390. fobj.clearChildNodes();
  391. fobjIter = null;
  392. }
  393. }
  394. }
  395. /** {@inheritDoc} */
  396. @Override
  397. public String toString() {
  398. return (super.toString() + (fobj != null ? "{fobj = " + fobj.toString() + "}" : ""));
  399. }
  400. /** {@inheritDoc} */
  401. @Override
  402. public void reset() {
  403. isFinished = false;
  404. curChildLM = null;
  405. childLMiter = new LMiter(this);
  406. /* Reset all the children LM that have been created so far. */
  407. for (LayoutManager childLM : getChildLMs()) {
  408. childLM.reset();
  409. }
  410. if (fobj != null) {
  411. markers = fobj.getMarkers();
  412. }
  413. lastGeneratedPosition = -1;
  414. }
  415. }