org.xmlactions.action.Action.java Source code

Java tutorial

Introduction

Here is the source code for org.xmlactions.action.Action.java

Source

/*
 * Copyright (C) Mike Murphy 2003-2015 <mike.murphy@xmlactions.org><mwjmurphy@gmail.com>
 *
 * Licensed 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 org.xmlactions.action;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.text.StrSubstitutor;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlactions.action.actions.BaseAction;
import org.xmlactions.action.actions.SetupBeanFromXML;
import org.xmlactions.action.config.ExecContext;
import org.xmlactions.action.config.IExecContext;
import org.xmlactions.common.io.ResourceCommon;
import org.xmlactions.common.io.ResourceUtils;
import org.xmlactions.common.text.Html;
import org.xmlactions.common.xml.BadXMLException;
import org.xmlactions.common.xml.XMLUtils;
import org.xmlactions.action.stackedpages.StackedPage;

/**
 * Parses pages pulling out the IPageAction tags, which are used to call java
 * code, insert data and other such actions.
 * 
 * @author MichaelMurphy
 * 
 */
public class Action {

    private static Logger log = LoggerFactory.getLogger(Action.class);

    // location of root folder
    private String rootPath;

    // page name to process, may contain addition path info
    private String pageName;

    public static char[][] EMPTY_NAMESPACE = { "".toCharArray() };

    // preceding name space for action, e.g <pager:code...> where pager is the
    // nameSpace
    public static char[][] nameSpaces = new char[0][];

    public Action() {
    }

    /**
     * 
     * @param nameSpace the nameSpace preceeding the action, e.g <pager:code...> where pager is the nameSpace
     */
    public Action(String nameSpace) {

        this.rootPath = null;
        this.pageName = null;
        nameSpaces = new char[0][];
        addNameSpace(nameSpace);
    }

    /**
     * 
     * @param rootPath
     *            the root path of the site
     * @param pageName
     *            the name of the page to load, may include path elements
     * @param nameSpace
     *            the nameSpace preceeding the action, e.g <pager:code...> where
     *            pager is the nameSpace
     */
    public Action(String rootPath, String pageName, String nameSpace) {
        this.rootPath = rootPath;
        this.pageName = pageName;
        nameSpaces = new char[0][];
        addNameSpace(nameSpace);
    }

    /**
     * 
     * @param rootPath
     *            the root path of the site
     * @param pageName
     *            the name of the page to load, may include path elements
     * @param nameSpace
     *            the nameSpace preceeding the action, e.g <pager:code...> where
     *            pager is the nameSpace
     */
    public Action(String rootPath, String pageName, char[][] nameSpaces) {
        this.rootPath = rootPath;
        this.pageName = pageName;
        this.nameSpaces = new char[0][];
        addNameSpaces(nameSpaces);
    }

    /**
     * Process the page replacing any actions and / or markers. TODO add the
     * markers replacement code. e.g. add code to replace ${...};
     * 
     * @param execContext
     * @return the processedPage result.
     * @throws IOException
     *             , NestedPagerException, ClassNotFoundException,
     *             InstantiationException, IllegalAccessException,
     *             InvocationTargetException, NoSuchMethodException
     */
    public String processPage(IExecContext execContext)
            throws IOException, NestedActionException, ClassNotFoundException, InstantiationException,
            IllegalAccessException, InvocationTargetException, NoSuchMethodException, BadXMLException {

        String pageName = StrSubstitutor.replace(getPageName(), execContext);

        String loadedPage = loadPage(getRootPath(), pageName);

        String page = processPage(execContext, loadedPage);

        // ===
        // Handle stacked page insert intos.
        // ===
        boolean continueProcessing = true;
        while (continueProcessing == true) {
            StackedPage stackedPage = StackedPage.getAndRemoveFirstStackedPage(execContext);
            if (stackedPage != null) {
                if (stackedPage.isRemove_html()) {
                    page = Html.removeOuterHtml(page);
                }
                execContext.put(stackedPage.getKey(), page);
                Action action = new Action(stackedPage.getPath(), stackedPage.getPage(),
                        stackedPage.getNamespace());
                page = action.processPage(execContext);
            } else {
                continueProcessing = false;
            }
        }

        Html html = new Html();
        page = html.removeOuterJsonOrXml(page);
        if (html.getContentType() != null) {
            execContext.put(ExecContext.CONTENT_TYPE_KEY, html.getContentType());
        }

        return page;
    }

    /*
     * Process the page replacing any actions and / or markers. TODO add the
     * markers replacement code. e.g. add code to replace ${...};
     * 
     * @throws IOException, NestedPagerException, ClassNotFoundException,
     * InstantiationException, IllegalAccessException,
     * InvocationTargetException, NoSuchMethodException
     */
    public String processPage(IExecContext execContext, String page)
            throws IOException, NestedActionException, ClassNotFoundException, InstantiationException,
            IllegalAccessException, InvocationTargetException, NoSuchMethodException, BadXMLException {
        if (StringUtils.isNotBlank(page)) {

            Map<String, Object> map = XMLUtils.findNameSpaces(page);
            List<String> pageNameSpaces = (List<String>) map.get(XMLUtils.MAP_KEY_URIS);
            addNameSpaces(pageNameSpaces);
            String actionMapName = (String) map.get(XMLUtils.MAP_KEY_XMLNS);

            List<ReplacementMarker> markers = findMarkers(page, getNameSpaces(), execContext, actionMapName);

            String newPage = replaceMarkers(execContext, page, markers);

            Object noStrSubst = execContext.get(ActionConst.NO_STR_SUBST);
            if (noStrSubst != null) {
                //execContext.put(ActionConst.NO_STR_SUBST, (Object) null);
                newPage = newPage.toString();
            } else {
                newPage = StrSubstitutor.replace(newPage.toString(), execContext);
            }

            Html html = new Html();
            newPage = html.removeOuterJsonOrXml(newPage);
            if (html.getContentType() != null) {
                execContext.put(ExecContext.CONTENT_TYPE_KEY, html.getContentType());
            }
            return newPage;
        }
        return page;
    }

    /**
     * Process an XML
     * 
     * @return an array of root actions, if size is 0 then no actions found.
     * 
     * @throws IOException
     *             , NestedPagerException, ClassNotFoundException,
     *             InstantiationException, IllegalAccessException,
     *             InvocationTargetException, NoSuchMethodException
     */
    public BaseAction[] processXML(IExecContext execContext, String page)
            throws IOException, NestedActionException, ClassNotFoundException, InstantiationException,
            IllegalAccessException, InvocationTargetException, NoSuchMethodException, BadXMLException {

        // log.debug("page:" + (page.length() > 300 ?
        // page.substring(0,300)+"\n..." : page));
        Map<String, Object> map = XMLUtils.findNameSpaces(page);
        List<String> pageNameSpaces = (List<String>) map.get(XMLUtils.MAP_KEY_URIS);
        addNameSpaces(pageNameSpaces);
        String actionMapName = (String) map.get(XMLUtils.MAP_KEY_XMLNS);

        List<ReplacementMarker> markers;
        if (getNameSpaces().length > 0) {
            markers = findMarkers(page, getNameSpaces(), execContext, actionMapName);
        } else {
            markers = findMarkers(page, execContext, actionMapName);
        }

        // is assumed that the first marker contains the root xml element action
        BaseAction[] rootActions = new BaseAction[markers.size()];
        for (int i = 0; i < markers.size(); i++) {
            rootActions[i] = markers.get(i).getAction();
        }
        return rootActions;
    }

    /**
     * 
     * @param rootPath
     *            the root path of the site
     * @param pageName
     *            the name of the page to load, may include path elements
     * @throws IOException
     */
    public static String loadPage(String rootPath, String pageName) throws IOException {

        // remove any data after ? which would be a parameter list in http language
        String pn = pageName;
        int indexOf = pageName.indexOf('?');
        if (indexOf >= 0) {
            pn = pageName.substring(0, indexOf);
        }

        String page;
        // log.debug("rootPath:" + rootPath + " pageName:" + pageName);
        String name = ResourceCommon.buildFileName(rootPath, pn);
        File file = new File(name);
        if (!file.exists()) {
            name = ResourceCommon.buildFileName(rootPath, pageName, "/");
            URL url = null;
            //url = Action.class.getResource(name);
            url = ResourceUtils.getResourceURL(name);
            // Validate.notNull(url, "Missing file [" + name + "]");
            page = IOUtils.toString(url.openStream());
        } else {
            page = FileUtils.readFileToString(file);
        }
        return page;
    }

    /**
     * Find and return a list of all the markers on the page that begin with the
     * nameSpace.
     * 
     * @param page
     *            the web page
     * @param nameSpaces
     *            the name space of the markers we want to find.
     * @param execContext
     * @param actionMapName
     * @return a list of ReplaceMarkers for each node found that uses the
     *         nameSpace
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws ClassNotFoundException
     * @throws NestedActionException
     * @throws BadXMLException
     * @throws DocumentException
     */
    public List<ReplacementMarker> findMarkers(String page, char[][] nameSpaces, IExecContext execContext,
            String actionMapName) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, NestedActionException, BadXMLException {
        Validate.notNull(nameSpaces,
                "The nameSpace value is not set, try setting this to \"pager\" before calling.");
        ActionMarkers actionMarkers = new ActionMarkers();
        List<ReplacementMarker> markers = actionMarkers.getReplacementList(pageName, page, nameSpaces, execContext,
                actionMapName);
        createActionHierarchy(null, markers);

        processMarkers(page, markers, execContext);

        return markers;
    }

    /**
     * Find and return a list of all the xml nodes on the page.
     * 
     * @param page
     *            the web page
     * @return a list of ReplaceMarkers for each node found that uses the
     *         nameSpace
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws ClassNotFoundException
     * @throws NestedActionException
     * @throws BadXMLException
     * @throws DocumentException
     */
    public List<ReplacementMarker> findMarkers(String page, IExecContext execContext, String actionMapName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, NestedActionException, BadXMLException {

        ActionMarkers actionMarkers = new ActionMarkers();
        List<ReplacementMarker> markers = actionMarkers.getReplacementList(page, execContext, actionMapName);
        createActionHierarchy(null, markers);

        processMarkers(page, markers, execContext);

        return markers;
    }

    /**
     * Associates child actions with their parents.
     * <p>
     * The markers contain the actions and the hierarchy order.
     * </p>
     * 
     * @param parent
     *            the parent action, will be null if the markers list is root.
     * @param markers
     *            the list of markers and their children used to provide the
     *            hierarchy list for the actions.
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private void createActionHierarchy(BaseAction parent, List<ReplacementMarker> markers)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {

        for (ReplacementMarker marker : markers) {
            if (parent != null) {
                // parent.addChild(marker.getAction());
                SetupBeanFromXML.setProperty(parent, marker.getXMLObject().getElementName(), marker.getAction());
                marker.getAction().setParent(parent);
            }
            createActionHierarchy(marker.getAction(), marker.getNestedMarkers());
        }
    }

    private void processMarkers(String pageContent, List<ReplacementMarker> markers, IExecContext execContext)
            throws NestedActionException {

        for (ReplacementMarker marker : markers) {
            BaseAction action = marker.getAction();
            try {
                action.processAction(pageContent, action, execContext);
            } catch (Exception ex) {
                log.error(ex.getMessage() + "\n" + pageContent, ex);
                throw new NestedActionException(
                        "Error Processing page [" + StrSubstitutor.replace(getPageName(), execContext) + "]", ex);
            }
        }
    }

    public String replaceMarkers(IExecContext execContext, String pageContent, List<ReplacementMarker> markers)
            throws NestedActionException {

        for (int iLoop = markers.size() - 1; iLoop >= 0; iLoop--) {
            ReplacementMarker marker = markers.get(iLoop);
            if (marker.getAction().isUsedForDisplay()) {
                String replacement = replaceMarkers(execContext, marker.getAction().getReplacementContent(),
                        marker.getNestedMarkers());
                if (replacement != null) {
                    if (marker.getAction() instanceof ReplacementHandlerAction) {
                        replacement = (String) ((ReplacementHandlerAction) marker.getAction())
                                .getReplacementData(execContext, replacement);
                    }
                    pageContent = marker.getAction().doReplace(pageContent, replacement, marker.getStart(),
                            marker.getEnd());
                }
            }
        }
        return (pageContent);
    }

    public String getRootPath() {

        return rootPath;
    }

    public void setRootPath(String rootPath) {

        this.rootPath = rootPath;
    }

    public String getPageName() {

        return pageName;
    }

    public void setPageName(String pageName) {

        this.pageName = pageName;
    }

    public char[][] getNameSpaces() {

        return nameSpaces;
    }

    public void setNameSpaces(char[][] nameSpaces) {

        this.nameSpaces = nameSpaces;
    }

    public void addNameSpaces(List<String> newNameSpaces) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < nameSpaces.length; i++) {
            String ns = new String(nameSpaces[i]);
            //if (StringUtils.isNotEmpty(ns)) {
            addNewNameSpace(list, ns);
            //}
        }
        int i = 0;
        for (String name : newNameSpaces) {
            //if (StringUtils.isNotEmpty(name)) {
            addNewNameSpace(list, name);
            //}
        }
        // nameSpaces = new char[list.size()][];
        i = 0;
        for (String name : list) {
            addNameSpace(name);
        }
    }

    public void addNameSpaces(char[][] newNameSpaces) {
        for (int i = 0; i < newNameSpaces.length; i++) {
            addNameSpace(newNameSpaces[i]);
        }
    }

    private void addNewNameSpace(List<String> list, String name) {
        for (String exists : list) {
            if (exists.equals(name)) {
                return;
            }
        }
        // log.debug("NAMESPACE.ADD:" + name);
        list.add(name);
    }

    public void addEmptyNameSpace() {
        if (getNameSpaces().length == 0) {
            setNameSpaces(EMPTY_NAMESPACE);
        } else {
            for (char[] n : getNameSpaces()) {
                if (n.length == 0) {
                    return; // we already have an empty namespace
                }
            }
            char[][] ns = new char[getNameSpaces().length + 1][];
            int index = 0;
            for (char[] n : getNameSpaces()) {
                ns[index++] = n;
            }
            ns[index] = "".toCharArray();
        }

    }

    /**
     * Add this namespace to the static list of namespaces [] []
     * 
     * If this namespace already exists in the list then it wont be added.
     * @param namespace - the namespace to add
     */
    public void addNameSpace(String nameSpace) {
        addNameSpace(nameSpace.toCharArray());
    }

    /**
     * Add this namespace to the static list of namespaces [] []
     * 
     * If this namespace already exists in the list then it wont be added.
     * @param nameSpace - the namespace to add
     */
    public void addNameSpace(char[] nameSpace) {

        char[][] newNameSpaces = new char[nameSpaces.length + 1][];
        for (int i = 0; i < nameSpaces.length; i++) {
            char[] ns = nameSpaces[i];
            if (nameSpace.toString().equals(ns.toString())) {
                return; // already exists so don't add it
            }
            newNameSpaces[i] = ns;
        }
        // add new nameSpace here
        newNameSpaces[newNameSpaces.length - 1] = nameSpace;
        nameSpaces = newNameSpaces;
    }

}