net.padaf.preflight.actions.ActionManagerFactory.java Source code

Java tutorial

Introduction

Here is the source code for net.padaf.preflight.actions.ActionManagerFactory.java

Source

/*******************************************************************************
 * Copyright 2010 Atos Worldline SAS
 * 
 * Licensed by Atos Worldline SAS under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Atos Worldline SAS 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.
 ******************************************************************************/
/**
 * 
 */
package net.padaf.preflight.actions;

import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_KEY_NEXT;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_KEY_S;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_GOTO;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_GOTOR;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_HIDE;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_IMPORT;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_JAVASCRIPT;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_LAUNCH;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_MOVIE;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_NAMED;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_NOOP;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_RESET;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_SETSTATE;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_SOUND;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_SUBMIT;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_THREAD;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_ATYPE_URI;
import static net.padaf.preflight.ValidationConstants.ACTION_DICTIONARY_VALUE_TYPE;
import static net.padaf.preflight.ValidationConstants.DICTIONARY_KEY_ACTION;
import static net.padaf.preflight.ValidationConstants.DICTIONARY_KEY_ADDITIONAL_ACTION;
import static net.padaf.preflight.ValidationConstants.DICTIONARY_KEY_OPEN_ACTION;
import static net.padaf.preflight.ValidationConstants.DICTIONARY_KEY_TYPE;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.padaf.preflight.ValidationException;
import net.padaf.preflight.utils.COSUtils;

import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSDocument;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.persistence.util.COSObjectKey;

public class ActionManagerFactory {
    //   /**
    //    * This map is used to know if an Action has already been validated. It is
    //    * useful to avoid infinite loop in an action which has a Next entry.
    //    */
    //   private Map<COSObjectKey, Boolean> alreadyCreated = new HashMap<COSObjectKey, Boolean>();

    /**
     * This method extract actions from the given dictionary. An action is
     * identified by the following entries :
     * <UL>
     * <li>A (Action) : Available in Annotations, Outline items
     * <li>OpenAction (OpenAction) : Available in the Catalog dictionary
     * <li>AA (Additional Action) : Available in the Catalog dictionary,
     * Annotations, Pages
     * </UL>
     * 
     * If there are no action, an empty list is returned.
     * 
     * @param dictionary
     * @param cDoc
     * @return
     * @throws ValidationException
     */
    public final List<AbstractActionManager> getActions(COSDictionary dictionary, COSDocument cDoc)
            throws ValidationException {

        List<AbstractActionManager> result = new ArrayList<AbstractActionManager>(0);
        Map<COSObjectKey, Boolean> alreadyCreated = new HashMap<COSObjectKey, Boolean>();

        COSBase aDict = dictionary.getDictionaryObject(DICTIONARY_KEY_ACTION);
        if (aDict != null) {
            callCreateAction(aDict, cDoc, result, alreadyCreated);
        }

        COSBase oaDict = dictionary.getDictionaryObject(DICTIONARY_KEY_OPEN_ACTION);
        if (oaDict != null) {
            if (!COSUtils.isArray(oaDict, cDoc)) {
                callCreateAction(oaDict, cDoc, result, alreadyCreated);
            }
            // else Nothing to do because of an array contains a Destination not an
            // action.
        }

        COSBase aa = dictionary.getDictionaryObject(DICTIONARY_KEY_ADDITIONAL_ACTION);
        if (aa != null) {
            COSDictionary aaDict = COSUtils.getAsDictionary(aa, cDoc);
            if (aaDict != null) {
                for (Object key : aaDict.keySet()) {
                    COSName name = (COSName) key;
                    callCreateAction(aaDict.getItem(name), cDoc, result, name.getName(), alreadyCreated);
                }
            }
        }
        return result;
    }

    /**
     * Call the callCreateAction(COSBase, COSDocument, List<ActionManager>,
     * String) method with null as isAA parameter.
     * 
     * @param aDict
     *          a COSBase object (COSObject or COSDictionary) which represent the
     *          action dictionary.
     * @param cDoc
     *          the COSDocument which contains the action.
     * @param result
     *          the list of ActionManager to updated if the aDict parameter is
     *          valid.
     * @param alreadyCreated This map is used to know if an Action has already been validated. It is
     * useful to avoid infinite loop in an action which has a Next entry.
     * @throws ValidationException
     */
    private void callCreateAction(COSBase aDict, COSDocument cDoc, List<AbstractActionManager> result,
            Map<COSObjectKey, Boolean> alreadyCreated) throws ValidationException {
        callCreateAction(aDict, cDoc, result, null, alreadyCreated);
    }

    /**
     * Call the create action to add the ActionManager to the result list. If the
     * aDict parameter isn't an instance of COSDictionary, this method throws a
     * ValdiationException. If the aDict parameter is a reference to a
     * COSDicitonary, the action manager is create only if the linked COSObjectKey
     * is missing from the "alreadyCreated" map, in this case the action is added
     * to the map. If the aDict parameter is an instance of COSDIctionary, it is
     * impossible to check if the ActionManager already exists in the
     * "alreadyCreated" map.
     * 
     * @param aDict
     *          a COSBase object (COSObject or COSDictionary) which represent the
     *          action dictionary.
     * @param cDoc
     *          the COSDocument which contains the action.
     * @param result
     *          the list of ActionManager to updated if the aDict parameter is
     *          valid.
     * @param additionActionKey
     *          the Action identifier if it is an additional action
      * @param alreadyCreated This map is used to know if an Action has already been validated. It is
     * useful to avoid infinite loop in an action which has a Next entry.
     * @throws ValidationException
     */
    private void callCreateAction(COSBase aDict, COSDocument cDoc, List<AbstractActionManager> result,
            String additionActionKey, Map<COSObjectKey, Boolean> alreadyCreated) throws ValidationException {
        if (COSUtils.isDictionary(aDict, cDoc)) {
            if (aDict instanceof COSObject) {
                COSObjectKey cok = new COSObjectKey((COSObject) aDict);
                if (!alreadyCreated.containsKey(cok)) {
                    result.add(createActionManager(COSUtils.getAsDictionary(aDict, cDoc), cDoc, additionActionKey));
                    alreadyCreated.put(cok, true);
                }
            } else {
                result.add(createActionManager(COSUtils.getAsDictionary(aDict, cDoc), cDoc, additionActionKey));
            }
        } else {
            throw new ValidationException("Action entry isn't an instance of COSDictionary");
        }
    }

    /**
     * Returns all actions contained by the Next entry. If the action dictionary
     * doesn't have Next action, the result is an empty list.
     * 
     * @param actionDictionary
     *          the action dictionary which contains Next entry
     * @param cDoc
     *          the COSDocument which contains actions.
     * @return
     * @throws ValidationException
     */
    public final List<AbstractActionManager> getNextActions(COSDictionary actionDictionary, COSDocument cDoc)
            throws ValidationException {
        List<AbstractActionManager> result = new ArrayList<AbstractActionManager>(0);
        Map<COSObjectKey, Boolean> alreadyCreated = new HashMap<COSObjectKey, Boolean>();

        COSBase nextDict = actionDictionary.getDictionaryObject(ACTION_DICTIONARY_KEY_NEXT);
        if (nextDict != null) {
            if (COSUtils.isArray(nextDict, cDoc)) {
                COSArray array = COSUtils.getAsArray(nextDict, cDoc);
                // ---- Next may contains an array of Action dictionary
                for (int i = 0; i < array.size(); ++i) {
                    callCreateAction(array.get(i), cDoc, result, alreadyCreated);
                }
            } else {
                // ---- Next field contains a Dictionary or a reference to a Dictionary
                callCreateAction(nextDict, cDoc, result, alreadyCreated);
            }
        }
        return result;
    }

    /**
     * Create an instance of ActionManager according to the value of the S entry.
     * If the type entry isn't Action, a ValidationException will be thrown.
     * 
     * If the action type isn't authorized in a PDF/A file, an instance of
     * InvalidAction is returned.
     * 
     * @param action
     *          the action dictionary used to instantiate the ActionManager
     * @param cDoc
     *          the COSDocument which contains the action
     * @param isAA
     *          the Action identifier if it is an additional action
     * @return
     * @throws ValidationException
     */
    protected AbstractActionManager createActionManager(COSDictionary action, COSDocument cDoc, String aaKey)
            throws ValidationException {

        String type = action.getNameAsString(DICTIONARY_KEY_TYPE);
        if (type != null && !ACTION_DICTIONARY_VALUE_TYPE.equals(type)) {
            throw new ValidationException("The given dictionary isn't the dictionary of an Action");
        }

        // ---- S is a mandatory fields. If S entry is missing, the return will
        // return the InvalidAction manager
        String s = action.getNameAsString(ACTION_DICTIONARY_KEY_S);

        // --- Here is authorized actions
        if (ACTION_DICTIONARY_VALUE_ATYPE_GOTO.equals(s)) {
            return new GoToAction(this, action, cDoc, aaKey);
        }

        if (ACTION_DICTIONARY_VALUE_ATYPE_GOTOR.equals(s)) {
            return new GoToRemoteAction(this, action, cDoc, aaKey);
        }

        if (ACTION_DICTIONARY_VALUE_ATYPE_THREAD.equals(s)) {
            return new ThreadAction(this, action, cDoc, aaKey);
        }

        if (ACTION_DICTIONARY_VALUE_ATYPE_URI.equals(s)) {
            return new UriAction(this, action, cDoc, aaKey);
        }

        if (ACTION_DICTIONARY_VALUE_ATYPE_HIDE.equals(s)) {
            return new HideAction(this, action, cDoc, aaKey);
        }

        if (ACTION_DICTIONARY_VALUE_ATYPE_NAMED.equals(s)) {
            return new NamedAction(this, action, cDoc, aaKey);
        }

        if (ACTION_DICTIONARY_VALUE_ATYPE_SUBMIT.equals(s)) {
            return new SubmitAction(this, action, cDoc, aaKey);
        }

        // --- Here is forbidden actions
        if (ACTION_DICTIONARY_VALUE_ATYPE_LAUNCH.equals(s) || ACTION_DICTIONARY_VALUE_ATYPE_SOUND.equals(s)
                || ACTION_DICTIONARY_VALUE_ATYPE_MOVIE.equals(s) || ACTION_DICTIONARY_VALUE_ATYPE_RESET.equals(s)
                || ACTION_DICTIONARY_VALUE_ATYPE_IMPORT.equals(s)
                || ACTION_DICTIONARY_VALUE_ATYPE_JAVASCRIPT.equals(s)
                || ACTION_DICTIONARY_VALUE_ATYPE_SETSTATE.equals(s)
                || ACTION_DICTIONARY_VALUE_ATYPE_NOOP.equals(s)) {
            return new InvalidAction(this, action, cDoc, aaKey, s);
        }

        // ---- The default ActionManager is the undefined one.
        // Actions defined in a PDF Reference greater than 1.4 will be considered as
        // Undefined actions, here the list of new actions until the PDF 1.6 :
        // #GoToE (1.6) : Not PDF/A, uses EmbeddedFiles.
        // # SetOCGState (1.5) : Not PDF/A, uses optional content.
        // # Rendition (1.5) : Not PDF/A, use multimedia content.
        // # Trans (1.5) : ??
        // # GoTo3DView (1.6) : ??
        return new UndefAction(this, action, cDoc, aaKey, s);
    }
}