/*
 * 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.render.intermediate.util;


import java.awt.Dimension;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;

import org.apache.xmlgraphics.xmp.Metadata;

import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFParser;

/**
 * This class allows to concatenate multiple intermediate format files to a single output file
 * in the final format. It is based on the SAX API and is therefore very fast and does not
 * require to load the individual documents into memory as DOM documents, for example.
 * <p>
 * Note: This class will filter/ignore any document navigation events. Support for this may be
 * added later.
 */
public class IFConcatenator {

    private IFDocumentHandler targetHandler;

    private int nextPageIndex = 0;

    /**
     * Creates a new IF concatenator.
     * @param targetHandler the target document handler
     * @param metadata the metadata object for the generated file (may be null)
     * @throws IFException if an IF-related error occurs
     */
    public IFConcatenator(IFDocumentHandler targetHandler, Metadata metadata) throws IFException {
        this.targetHandler = targetHandler;
        startDocument(metadata);
    }

    private void startDocument(Metadata metadata) throws IFException {
        this.targetHandler.startDocument();
        this.targetHandler.startDocumentHeader();
        if (metadata != null) {
            this.targetHandler.handleExtensionObject(metadata);
        }
        this.targetHandler.endDocumentHeader();
    }

    private void endDocument() throws IFException {
        this.targetHandler.startPageTrailer();
        this.targetHandler.endPageTrailer();
        this.targetHandler.endDocument();
    }

    /**
     * Returns the target document handler.
     * @return the target document handler
     */
    protected IFDocumentHandler getTargetHandler() {
        return this.targetHandler;
    }

    /**
     * Properly finishes the current output file by creating an empty document trailer and calling
     * {@link IFDocumentHandler#endDocument()}.
     * @throws IFException if an IF-related error occurs
     */
    public void finish() throws IFException {
        endDocument();
    }

    /**
     * Appends another intermediate format document to the current output file. All document-level
     * content (i.e. the document header and trailer) is ignored. This method shall not be called
     * after {@link #finish()} has been called.
     * @param src the JAXP Source identifying the input document
     * @throws TransformerException if an XML-related exception occurs during
     * @throws IFException if an IF-related error occurs
     */
    public void appendDocument(Source src) throws TransformerException, IFException {
        IFParser parser = new IFParser();
        parser.parse(src, new IFPageSequenceFilter(getTargetHandler()),
                getTargetHandler().getContext().getUserAgent());
    }

    private class IFPageSequenceFilter extends IFDocumentHandlerProxy {

        private boolean inPageSequence = false;

        public IFPageSequenceFilter(IFDocumentHandler delegate) {
            super(delegate);
        }

        /** {@inheritDoc} */
        public void startDocument() throws IFException {
            //ignore
        }

        /** {@inheritDoc} */
        public void startDocumentHeader() throws IFException {
            //ignore
        }

        /** {@inheritDoc} */
        public void endDocumentHeader() throws IFException {
            //ignore
        }

        /** {@inheritDoc} */
        public void startPageSequence(String id) throws IFException {
            assert !this.inPageSequence;
            this.inPageSequence = true;

            super.startPageSequence(id);
        }

        /** {@inheritDoc} */
        public void startPage(int index, String name, String pageMasterName, Dimension size)
                throws IFException {
            //Adjust page indices
            super.startPage(nextPageIndex, name, pageMasterName, size);
            nextPageIndex++;
        }

        /** {@inheritDoc} */
        public void endPageSequence() throws IFException {
            super.endPageSequence();

            assert this.inPageSequence;
            this.inPageSequence = false;
        }

        /** {@inheritDoc} */
        public void startDocumentTrailer() throws IFException {
            //ignore
        }

        /** {@inheritDoc} */
        public void endDocumentTrailer() throws IFException {
            //ignore
        }

        /** {@inheritDoc} */
        public void endDocument() throws IFException {
            //ignore
        }

        /** {@inheritDoc} */
        public void handleExtensionObject(Object extension) throws IFException {
            if (inPageSequence) {
                //Only pass through when inside page-sequence
                super.handleExtensionObject(extension);
            }
        }

        /** {@inheritDoc} */
        public IFDocumentNavigationHandler getDocumentNavigationHandler() {
            return null; //Document Navigation is filtered!!!
        }

    }
}

@@ -859,17 +859,16 @@
 .util;%0A%0A
-%0A
 import j
@@ -1652,16 +1652,272 @@
  later.%0A
+ * %3Cp%3E%0A * Note: document-level extensions will only be transferred from the first document passed in.%0A * If you need to merge extensions from all the concatenated documents, you may have to merge%0A * these manually on the XML level, for example using XSLT.%0A
  */%0Apubl
@@ -2023,16 +2023,60 @@
 dex = 0;
+%0A    private boolean inFirstDocument = true;
 %0A%0A    /*
@@ -5938,32 +5938,69 @@
        //ignore%0A
+            inFirstDocument = false;%0A
         %7D%0A%0A     
@@ -6134,16 +6134,35 @@
 Sequence
+ %7C%7C inFirstDocument
 ) %7B%0A    
@@ -6239,61 +6239,206 @@
     
-super.handleExtensionObject(extension);%0A            %7D
+//or for the first document (for document-level extensions).%0A                super.handleExtensionObject(extension);%0A            %7D%0A            //Note:Extensions from non-first documents are ignored!
 %0A   

Allow to pass through document-level extensions from the first document passed to the IFConcatenator.