blob: 0986891f85b35a56438dbabb7fdfd7825424ad6d [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.area;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Used by the AreaTreeHandler to keep track of ID reference usage
* on a PageViewport level.
*/
public class IDTracker {
private static final Log LOG = LogFactory.getLog(IDTracker.class);
// Map of ID's whose area is located on one or more consecutive
// PageViewports. Each ID has a list of PageViewports that
// form the defined area of this ID
private Map<String, List<PageViewport>> idLocations
= new java.util.HashMap<String, List<PageViewport>>();
// idref's whose target PageViewports have yet to be identified
// Each idref has a HashSet of Resolvable objects containing that idref
private Map<String, Set<Resolvable>> unresolvedIDRefs
= new java.util.HashMap<String, Set<Resolvable>>();
private Set<String> unfinishedIDs = new java.util.HashSet<String>();
private Set<String> alreadyResolvedIDs = new java.util.HashSet<String>();
/**
* Tie a PageViewport with an ID found on a child area of the PV. Note that
* an area with a given ID may be on more than one PV, hence an ID may have
* more than one PV associated with it.
*
* @param id the property ID of the area
* @param pv a page viewport that contains the area with this ID
*/
public void associateIDWithPageViewport(String id, PageViewport pv) {
if (LOG.isDebugEnabled()) {
LOG.debug("associateIDWithPageViewport(" + id + ", " + pv + ")");
}
List<PageViewport> pvList = idLocations.get(id);
if (pvList == null) { // first time ID located
pvList = new java.util.ArrayList<PageViewport>();
idLocations.put(id, pvList);
pvList.add(pv);
// signal the PageViewport that it is the first PV to contain this id:
pv.setFirstWithID(id);
/*
* See if this ID is in the unresolved idref list, if so resolve
* Resolvable objects tied to it.
*/
if (!unfinishedIDs.contains(id)) {
tryIDResolution(id, pvList);
}
} else {
/* TODO: The check is a quick-fix to avoid a waste
* when adding inline-ids to the page */
if (!pvList.contains(pv)) {
pvList.add(pv);
}
}
}
/**
* This method tie an ID to the areaTreeHandler until this one is ready to
* be processed. This is used in page-number-citation-last processing so we
* know when an id can be resolved.
*
* @param id the id of the object being processed
*/
public void signalPendingID(String id) {
if (LOG.isDebugEnabled()) {
LOG.debug("signalPendingID(" + id + ")");
}
unfinishedIDs.add(id);
}
/**
* Signals that all areas for the formatting object with the given ID have
* been generated. This is used to determine when page-number-citation-last
* ref-ids can be resolved.
*
* @param id the id of the formatting object which was just finished
*/
public void signalIDProcessed(String id) {
if (LOG.isDebugEnabled()) {
LOG.debug("signalIDProcessed(" + id + ")");
}
alreadyResolvedIDs.add(id);
if (!unfinishedIDs.contains(id)) {
return;
}
unfinishedIDs.remove(id);
List<PageViewport> idLocs = idLocations.get(id);
Set<Resolvable> todo = unresolvedIDRefs.get(id);
if (todo != null) {
for (Resolvable res : todo) {
res.resolveIDRef(id, idLocs);
}
unresolvedIDRefs.remove(id);
}
}
/**
* Check if an ID has already been resolved
*
* @param id the id to check
* @return true if the ID has been resolved
*/
public boolean alreadyResolvedID(String id) {
return (alreadyResolvedIDs.contains(id));
}
/**
* Tries to resolve all unresolved ID references on the given set of pages.
*
* @param id ID to resolve
* @param pvList list of PageViewports
*/
private void tryIDResolution(String id, List<PageViewport> pvList) {
Set<Resolvable> todo = unresolvedIDRefs.get(id);
if (todo != null) {
for (Resolvable res : todo) {
if (!unfinishedIDs.contains(id)) {
res.resolveIDRef(id, pvList);
} else {
return;
}
}
alreadyResolvedIDs.add(id);
unresolvedIDRefs.remove(id);
}
}
/**
* Tries to resolve all unresolved ID references on the given page.
*
* @param pv page viewport whose ID refs to resolve
*/
public void tryIDResolution(PageViewport pv) {
String[] ids = pv.getIDRefs();
if (ids != null) {
for (String id : ids) {
List<PageViewport> pvList = idLocations.get(id);
if (!(pvList == null || pvList.isEmpty())) {
tryIDResolution(id, pvList);
}
}
}
}
/**
* Get the list of page viewports that have an area with a given id.
*
* @param id the id to lookup
* @return the list of PageViewports
*/
public List<PageViewport> getPageViewportsContainingID(String id) {
if (!(idLocations == null || idLocations.isEmpty())) {
List<PageViewport> idLocs = idLocations.get(id);
if (idLocs != null) {
return idLocs;
}
}
return Collections.emptyList();
}
/**
* Get the first {@link PageViewport} containing content generated
* by the FO with the given {@code id}.
*
* @param id the id
* @return the first {@link PageViewport} for the id; {@code null} if
* no matching {@link PageViewport} was found
*/
public PageViewport getFirstPageViewportContaining(String id) {
List<PageViewport> list = getPageViewportsContainingID(id);
if (!(list == null || list.isEmpty())) {
return list.get(0);
}
return null;
}
/**
* Get the last {@link PageViewport} containing content generated
* by the FO with the given {@code id}.
*
* @param id the id
* @return the last {@link PageViewport} for the id; {@code null} if
* no matching {@link PageViewport} was found
*/
public PageViewport getLastPageViewportContaining(String id) {
List<PageViewport> list = getPageViewportsContainingID(id);
if (!(list == null || list.isEmpty())) {
return list.get(list.size() - 1);
}
return null;
}
/**
* Add an Resolvable object with an unresolved idref
*
* @param idref the idref whose target id has not yet been located
* @param res the Resolvable object needing the idref to be resolved
*/
public void addUnresolvedIDRef(String idref, Resolvable res) {
Set<Resolvable> todo = unresolvedIDRefs.get(idref);
if (todo == null) {
todo = new java.util.HashSet<Resolvable>();
unresolvedIDRefs.put(idref, todo);
}
// add Resolvable object to this HashSet
todo.add(res);
}
}