blob: ee78bb8ba84d126fe567e12d27d6c0fec14e3466 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.fo.pagination;
// Java
import java.util.Map;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
/**
* Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_page-sequence">
* <code>fo:page-sequence</code></a> object.
*/
public class PageSequence extends AbstractPageSequence {
// The value of properties relevant for fo:page-sequence.
private String country;
private String language;
private String masterReference;
//private int writingMode; //XSL 1.1
// End of property values
// There doesn't seem to be anything in the spec requiring flows
// to be in the order given, only that they map to the regions
// defined in the page sequence, so all we need is this one hashmap
// the set of flows includes StaticContent flows also
/** Map of flows to their flow name (flow-name, Flow) */
private Map<String, Flow> flowMap;
/**
* The currentSimplePageMaster is either the page master for the
* whole page sequence if master-reference refers to a simple-page-master,
* or the simple page master produced by the page sequence master otherwise.
* The pageSequenceMaster is null if master-reference refers to a
* simple-page-master.
*/
private SimplePageMaster simplePageMaster;
private PageSequenceMaster pageSequenceMaster;
/**
* The fo:title object for this page-sequence.
*/
private Title titleFO;
/**
* The fo:flow object for this page-sequence.
*/
private Flow mainFlow = null;
/**
* Create a PageSequence instance that is a child of the
* given {@link FONode}.
*
* @param parent the parent {@link FONode}
*/
public PageSequence(FONode parent) {
super(parent);
}
/** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
country = pList.get(PR_COUNTRY).getString();
language = pList.get(PR_LANGUAGE).getString();
masterReference = pList.get(PR_MASTER_REFERENCE).getString();
//writingMode = pList.getWritingMode();
if (masterReference == null || masterReference.equals("")) {
missingPropertyError("master-reference");
}
}
/** {@inheritDoc} */
protected void startOfNode() throws FOPException {
super.startOfNode();
flowMap = new java.util.HashMap<String, Flow>();
this.simplePageMaster
= getRoot().getLayoutMasterSet().getSimplePageMaster(masterReference);
if (simplePageMaster == null) {
this.pageSequenceMaster
= getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference);
if (pageSequenceMaster == null) {
getFOValidationEventProducer().masterNotFound(this, getName(),
masterReference, getLocator());
}
}
getFOEventHandler().startPageSequence(this);
}
/** {@inheritDoc} */
protected void endOfNode() throws FOPException {
if (mainFlow == null) {
missingChildElementError("(title?,static-content*,flow)");
}
getFOEventHandler().endPageSequence(this);
}
/**
* {@inheritDoc}
XSL Content Model: (title?,static-content*,flow)
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
if (FO_URI.equals(nsURI)) {
if ("title".equals(localName)) {
if (titleFO != null) {
tooManyNodesError(loc, "fo:title");
} else if (!flowMap.isEmpty()) {
nodesOutOfOrderError(loc, "fo:title", "fo:static-content");
} else if (mainFlow != null) {
nodesOutOfOrderError(loc, "fo:title", "fo:flow");
}
} else if ("static-content".equals(localName)) {
if (mainFlow != null) {
nodesOutOfOrderError(loc, "fo:static-content", "fo:flow");
}
} else if ("flow".equals(localName)) {
if (mainFlow != null) {
tooManyNodesError(loc, "fo:flow");
}
} else {
invalidChildError(loc, nsURI, localName);
}
}
}
/**
* {@inheritDoc}
* TODO see if addChildNode() should also be called for fo's other than
* fo:flow.
*/
public void addChildNode(FONode child) throws FOPException {
int childId = child.getNameId();
switch (childId) {
case FO_TITLE:
this.titleFO = (Title)child;
break;
case FO_FLOW:
this.mainFlow = (Flow)child;
addFlow(mainFlow);
break;
case FO_STATIC_CONTENT:
addFlow((StaticContent)child);
flowMap.put(((Flow)child).getFlowName(), (Flow)child);
break;
default:
super.addChildNode(child);
}
}
/**
* Add a flow or static content, mapped by its flow-name.
* The flow-name is used to associate the flow with a region on a page,
* based on the region-names given to the regions in the page-master
* used to generate that page.
* @param flow the {@link Flow} instance to be added
* @throws org.apache.fop.fo.ValidationException if the fo:flow maps
* to an invalid page-region
*/
private void addFlow(Flow flow) throws ValidationException {
String flowName = flow.getFlowName();
if (hasFlowName(flowName)) {
getFOValidationEventProducer().duplicateFlowNameInPageSequence(this, flow.getName(),
flowName, flow.getLocator());
}
if (!getRoot().getLayoutMasterSet().regionNameExists(flowName)
&& !flowName.equals("xsl-before-float-separator")
&& !flowName.equals("xsl-footnote-separator")) {
getFOValidationEventProducer().flowNameNotMapped(this, flow.getName(),
flowName, flow.getLocator());
}
}
/**
* Get the static content FO node from the flow map.
* This gets the static content flow for the given flow name.
*
* @param name the flow name to find
* @return the static content FO node
*/
public StaticContent getStaticContent(String name) {
return (StaticContent) flowMap.get(name);
}
/**
* Accessor method for the fo:title associated with this fo:page-sequence
* @return titleFO for this object
*/
public Title getTitleFO() {
return titleFO;
}
/**
* Public accessor for getting the MainFlow to which this PageSequence is
* attached.
* @return the MainFlow object to which this PageSequence is attached.
*/
public Flow getMainFlow() {
return mainFlow;
}
/**
* Determine if this PageSequence already has a flow with the given flow-name
* Used for validation of incoming fo:flow or fo:static-content objects
* @param flowName The flow-name to search for
* @return true if flow-name already defined within this page sequence,
* false otherwise
*/
public boolean hasFlowName(String flowName) {
return flowMap.containsKey(flowName);
}
/** @return the flow map for this page-sequence */
public Map<String, Flow> getFlowMap() {
return this.flowMap;
}
/**
* Public accessor for determining the next page master to use within this page sequence.
* @param page the page number of the page to be created
* @param isFirstPage indicator whether this page is the first page of the
* page sequence
* @param isLastPage indicator whether this page is the last page of the
* page sequence
* @param isBlank indicator whether the page will be blank
* @return the SimplePageMaster to use for this page
* @throws PageProductionException if there's a problem determining the page master
*/
public SimplePageMaster getNextSimplePageMaster
(int page, boolean isFirstPage, boolean isLastPage, boolean isBlank)
throws PageProductionException {
if (pageSequenceMaster == null) {
return simplePageMaster;
}
boolean isOddPage = ((page % 2) == 1);
if (log.isDebugEnabled()) {
log.debug("getNextSimplePageMaster(page=" + page
+ " isOdd=" + isOddPage
+ " isFirst=" + isFirstPage
+ " isLast=" + isLastPage
+ " isBlank=" + isBlank + ")");
}
return pageSequenceMaster.getNextSimplePageMaster(isOddPage,
isFirstPage, isLastPage, isBlank);
}
/**
* Used to set the "cursor position" for the page masters to the previous item.
* @return true if there is a previous item, false if the current one was the first one.
*/
public boolean goToPreviousSimplePageMaster() {
return pageSequenceMaster == null || pageSequenceMaster.goToPreviousSimplePageMaster();
}
/** @return true if the page-sequence has a page-master with page-position="last" */
public boolean hasPagePositionLast() {
return pageSequenceMaster != null && pageSequenceMaster.hasPagePositionLast();
}
/** @return true if the page-sequence has a page-master with page-position="only" */
public boolean hasPagePositionOnly() {
return pageSequenceMaster != null && pageSequenceMaster.hasPagePositionOnly();
}
/**
* Get the value of the <code>master-reference</code> property.
* @return the "master-reference" property
*/
public String getMasterReference() {
return masterReference;
}
/** {@inheritDoc} */
public String getLocalName() {
return "page-sequence";
}
/**
* {@inheritDoc}
* @return {@link org.apache.fop.fo.Constants#FO_PAGE_SEQUENCE}
*/
public int getNameId() {
return FO_PAGE_SEQUENCE;
}
/**
* Get the value of the <code>country</code> property.
* @return the country property value
*/
public String getCountry() {
return this.country;
}
/**
* Get the value of the <code>language</code> property.
* @return the language property value
*/
public String getLanguage() {
return this.language;
}
/**
* Releases a page-sequence's children after the page-sequence has been fully processed.
*/
public void releasePageSequence() {
this.mainFlow = null;
this.flowMap.clear();
}
}