org.apache.fop.render.rtf.rtflib.tools.BuilderContext Maven / Gradle / Ivy
/*
* 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: BuilderContext.java 1537600 2013-10-31 19:44:25Z gadams $ */
package org.apache.fop.render.rtf.rtflib.tools;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.FObj;
import org.apache.fop.render.rtf.RTFHandler;
import org.apache.fop.render.rtf.RTFPlaceHolderHelper;
import org.apache.fop.render.rtf.rtflib.exceptions.RtfException;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfOptions;
import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer;
/**
* A BuilderContext holds context information when building an RTF document.
*
* This class was originally developed by Bertrand Delacretaz [email protected]
* for the JFOR project and is now integrated into FOP.
*
* This work was authored by Bertrand Delacretaz ([email protected]),
* Andreas Putz ([email protected]), and
* Peter Herweg ([email protected]).
*/
public class BuilderContext {
/** Static logging instance */
protected static final Log LOG = LogFactory.getLog(BuilderContext.class.getName());
/** stack of RtfContainers */
private final Stack containers = new Stack();
/** stack of TableContexts */
private final Stack tableContexts = new Stack();
/** stack of IBuilders */
private final Stack builders = new Stack();
/** Rtf options */
private IRtfOptions options;
/**
* Construct a builder context.
* @param rtfOptions some options
*/
public BuilderContext(IRtfOptions rtfOptions) {
options = rtfOptions;
}
/** find first object of given class from top of stack s
* @return null if not found
*/
private Object getObjectFromStack(Stack s, Class desiredClass) {
Object result = null;
final Stack copy = (Stack)s.clone();
while (!copy.isEmpty()) {
final Object o = copy.pop();
if (desiredClass.isAssignableFrom(o.getClass())) {
result = o;
break;
}
}
return result;
}
/* find the "nearest" IBuilder of given class /
public Object getBuilder(Class builderClass,boolean required)
throws Exception
{
final IBuilder result = (IBuilder)getObjectFromStack(builders,builderClass);
if(result == null && required) {
throw new Exception(
"IBuilder of class '" + builderClass.getName() + "' not found on builders stack"
);
}
return result;
}*/
/**
* Find the "nearest" container that implements the given interface on our stack.
* @param containerClass class of container
* @param required if true, ConverterException is thrown if no container found
* @param forWhichBuilder used in error message if container not found
* @return the container
* @throws RtfException if not caught
*/
public RtfContainer getContainer(Class containerClass, boolean required,
Object forWhichBuilder) throws RtfException {
// TODO what to do if the desired container is not at the top of the stack?
// close top-of-stack container?
RtfContainer result = (RtfContainer)getObjectFromStack(containers,
containerClass);
if (result == null && required) {
RTFPlaceHolderHelper placeHolderHelper = new RTFPlaceHolderHelper(this);
placeHolderHelper.createRTFPlaceholder(containerClass);
result = getContainer(containerClass, required, forWhichBuilder);
if (result == null) {
throw new RtfException(
"No RtfContainer of class '" + containerClass.getName()
+ "' available for '" + forWhichBuilder.getClass().getName() + "' builder"
);
}
}
return result;
}
/**
* Push an RtfContainer on our stack.
* @param c the container
*/
public void pushContainer(RtfContainer c) {
containers.push(c);
}
/**
* Push a Class representing a non-writeable section of the FO tree
* @param part the part
*/
public void pushPart(FObj part) {
containers.push(part);
}
/**
* In some cases an RtfContainer must be replaced by another one on the
* stack. This happens when handling nested fo:blocks for example: after
* handling a nested block the enclosing block must switch to a new
* paragraph container to handle what follows the nested block.
* TODO: what happens to elements that are "more on top" than oldC on the
* stack? shouldn't they be closed or something?
* @param oldC old container
* @param newC new container
* @throws Exception if not caught
*/
public void replaceContainer(RtfContainer oldC, RtfContainer newC)
throws Exception {
// treating the Stack as a Vector allows such manipulations (yes, I hear you screaming ;-)
final int index = containers.indexOf(oldC);
if (index < 0) {
throw new Exception("container to replace not found:" + oldC);
}
containers.setElementAt(newC, index);
}
/** pop the topmost RtfContainer from our stack */
public void popContainer(Class containerClass, RTFHandler handler) {
handlePop(containerClass, handler);
}
/** pop the topmost part class from our stack */
public void popPart(Class part, RTFHandler handler) {
handlePop(part, handler);
}
/**
* This method checks for any tag mismatches between what is being closed
* and what exists on the stack. If a mismatch is found, then it will push
* the object back onto the stack and attempt to close the given section
* before retrying with the current pop task.
* @param aClass The class to be popped from the stack
* @param handler The RTF handler class to fix any mismatches
*/
private void handlePop(Class aClass, RTFHandler handler) {
Object object = containers.pop();
if (object.getClass() != aClass) {
pushAndClose(aClass, object, handler);
}
}
private void pushAndClose(Class aClass, Object object, RTFHandler handler) {
containers.push(object);
if (handler.endContainer(object.getClass())) {
popContainer(aClass, handler);
} else {
/* This should never happen unless a placeholder is not catered for
* in the RTFHandler.endContainer method. */
LOG.warn("Unhandled RTF structure tag mismatch detected between "
+ aClass.getSimpleName() + " and " + object.getClass().getSimpleName());
}
}
/* push an IBuilder to our stack /
public void pushBuilder(IBuilder b)
{
builders.push(b);
}*/
/** pop the topmost IBuilder from our stack and return previous builder on stack
* @return null if builders stack is empty
public IBuilder popBuilderAndGetPreviousOne()
{
IBuilder result = null;
builders.pop();
if(!builders.isEmpty()) {
result = (IBuilder)builders.peek();
}
return result;
}
*/
/** @return the current TableContext */
public TableContext getTableContext() {
return (TableContext)tableContexts.peek();
}
/**
* Push a TableContext to our stack.
* @param tc the table context
*/
public void pushTableContext(TableContext tc) {
tableContexts.push(tc);
}
/**
* Pop a TableContext from our stack.
*/
public void popTableContext() {
tableContexts.pop();
}
}