All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.astamuse.asta4d.template.TemplateUtil Maven / Gradle / Ivy

Go to download

core functionalities of asta4d framework, including template and snippt implemention

There is a newer version: 1.2-M2
Show newest version
/*
 * Copyright 2012 astamuse company,Ltd.
 * 
 * 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 com.astamuse.asta4d.template;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jsoup.helper.StringUtil;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.astamuse.asta4d.Configuration;
import com.astamuse.asta4d.extnode.ExtNode;
import com.astamuse.asta4d.extnode.ExtNodeConstants;
import com.astamuse.asta4d.extnode.GroupNode;
import com.astamuse.asta4d.util.IdGenerator;
import com.astamuse.asta4d.util.SelectorUtil;

public class TemplateUtil {

    private static class SnippetNode extends ExtNode {

        /**
         * 
         * @param renderer
         *            a plain text renderer declaration
         */
        public SnippetNode(String renderer) {
            super(ExtNodeConstants.SNIPPET_NODE_TAG);
            this.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS, ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS_READY);
            this.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_RENDER, renderer);
        }

    }

    private final static Logger logger = LoggerFactory.getLogger(TemplateUtil.class);

    public final static void regulateElement(String path, Document doc) throws TemplateException, TemplateNotFoundException {
        // disabled. see {@link #loadStaticEmebed}
        // load static embed at first
        // loadStaticEmebed(doc);

        regulateSnippets(path, doc);
        regulateMsgs(path, doc);
        regulateEmbed(doc);
    }

    private final static String createSnippetRef() {
        return "sn-" + IdGenerator.createId();
    }

    private final static void regulateMsgs(String path, Document doc) {
        List msgElems = doc.select(ExtNodeConstants.MSG_NODE_TAG_SELECTOR);
        for (Element element : msgElems) {
            // record template path
            if (!element.hasAttr(ExtNodeConstants.ATTR_TEMPLATE_PATH)) {
                element.attr(ExtNodeConstants.ATTR_TEMPLATE_PATH, path);
            }
        }
    }

    private final static void regulateSnippets(String path, Document doc) {

        // find nodes emebed with snippet attribute
        String snippetSelector = SelectorUtil.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_RENDER_WITH_NS);
        snippetSelector = SelectorUtil.not(snippetSelector, ExtNodeConstants.SNIPPET_NODE_TAG_SELECTOR);

        List embedSnippets = new ArrayList<>(doc.select(snippetSelector));
        // Element
        // Node parent;
        SnippetNode fakedSnippetNode;
        String render;
        for (Element element : embedSnippets) {

            render = element.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_RENDER_WITH_NS);
            fakedSnippetNode = new SnippetNode(render);
            fakedSnippetNode.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE, ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE_FAKE);

            // move the original node under the faked node
            element.after(fakedSnippetNode);
            element.remove();
            fakedSnippetNode.appendChild(element);
            element.removeAttr(ExtNodeConstants.SNIPPET_NODE_ATTR_RENDER_WITH_NS);

            // set parallel type
            if (element.hasAttr(ExtNodeConstants.SNIPPET_NODE_ATTR_PARALLEL_WITH_NS)) {
                fakedSnippetNode.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_PARALLEL, "");
                element.removeAttr(ExtNodeConstants.SNIPPET_NODE_ATTR_PARALLEL_WITH_NS);
            }
        }

        /*
         * set all the nodes without status attribute or with an illegal status 
         * value to ready 
         */

        // first, we regulate the snippets to legal form
        List snippetNodes = doc.select(ExtNodeConstants.SNIPPET_NODE_TAG_SELECTOR);
        String status;
        for (Element sn : snippetNodes) {
            // record template path
            if (!sn.hasAttr(ExtNodeConstants.ATTR_TEMPLATE_PATH)) {
                sn.attr(ExtNodeConstants.ATTR_TEMPLATE_PATH, path);
            }
            // regulate status
            if (sn.hasAttr(ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS)) {
                status = sn.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS);
                switch (status) {
                case ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS_READY:
                case ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS_WAITING:
                case ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS_FINISHED:
                    // do nothing;
                    break;
                default:
                    sn.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS, ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS_READY);
                }
            } else {
                sn.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS, ExtNodeConstants.SNIPPET_NODE_ATTR_STATUS_READY);
            }
            // regulate id
            if (!sn.hasAttr(ExtNodeConstants.ATTR_SNIPPET_REF)) {
                sn.attr(ExtNodeConstants.ATTR_SNIPPET_REF, createSnippetRef());
            }
            // regulate type
            if (!sn.hasAttr(ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE)) {
                sn.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE, ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE_USERDEFINE);
            }
            switch (sn.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE)) {
            case ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE_FAKE:
            case ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE_USERDEFINE:
                // do nothing;
                break;
            default:
                // we do not allow snippet node has user customized type
                // attribute
                sn.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE, ExtNodeConstants.SNIPPET_NODE_ATTR_TYPE_USERDEFINE);
            }
        }

        // then let us check the nested relation for nodes without block attr
        snippetSelector = SelectorUtil.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_BLOCK);
        snippetSelector = SelectorUtil.not(ExtNodeConstants.SNIPPET_NODE_TAG_SELECTOR, snippetSelector);
        snippetNodes = doc.select(snippetSelector);
        setBlockingParentSnippetId(snippetNodes);
    }

    private final static void regulateEmbed(Document doc) throws TemplateException, TemplateNotFoundException {
        // check nodes without block attr for blocking parent snippets
        String selector = SelectorUtil.attr(ExtNodeConstants.EMBED_NODE_ATTR_BLOCK);
        selector = SelectorUtil.not(ExtNodeConstants.EMBED_NODE_TAG_SELECTOR, selector);
        List embedElemes = doc.select(selector);
        setBlockingParentSnippetId(embedElemes);
    }

    /**
     * Disabled static embed at 2014.09.26.
     * 
     * Developers would like to use different snippets to render a same static embed file as following: 
     *   
     *      
     *   
     * 
     * 
     * Which confuses rendering logic and makes bad source smell, thus we decide to disable this feature.
     * 
     * @param doc
     * @throws TemplateException
     * @throws TemplateNotFoundException
     */
    @SuppressWarnings("unused")
    @Deprecated
    private final static void loadStaticEmebed(Document doc) throws TemplateException, TemplateNotFoundException {

        String selector = SelectorUtil.attr(SelectorUtil.tag(ExtNodeConstants.EMBED_NODE_TAG_SELECTOR),
                ExtNodeConstants.EMBED_NODE_ATTR_STATIC, null);

        int embedNodeListCount;
        do {
            List embedNodeList = doc.select(selector);
            embedNodeListCount = embedNodeList.size();
            Iterator embedNodeIterator = embedNodeList.iterator();
            Element embed;
            Element embedContent;
            while (embedNodeIterator.hasNext()) {
                embed = embedNodeIterator.next();
                embedContent = getEmbedNodeContent(embed);
                mergeBlock(doc, embedContent);
                embed.before(embedContent);
                embed.remove();
            }
        } while (embedNodeListCount > 0);

    }

    private final static void setBlockingParentSnippetId(List elems) {
        Element searchElem;
        String blockingParentId;
        for (Element elem : elems) {
            searchElem = elem.parent();
            blockingParentId = "";
            while (searchElem != null) {
                if (searchElem.tagName().equals(ExtNodeConstants.SNIPPET_NODE_TAG)) {
                    blockingParentId = searchElem.attr(ExtNodeConstants.ATTR_SNIPPET_REF);
                    break;
                } else {
                    searchElem = searchElem.parent();
                }
            }
            elem.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_BLOCK, blockingParentId);
            // TODO should we replace the blocked element to a dummy place
            // holder element to avoid being rendered by parent snippets
        }
    }

    public final static void resetSnippetRefs(Element elem) {
        String snippetRefSelector = SelectorUtil.attr(ExtNodeConstants.ATTR_SNIPPET_REF);
        List snippets = new ArrayList<>(elem.select(snippetRefSelector));
        String oldRef, newRef;
        String blockedSnippetSelector;
        List blockedSnippets;
        for (Element element : snippets) {
            oldRef = element.attr(ExtNodeConstants.ATTR_SNIPPET_REF);
            newRef = createSnippetRef();

            // find blocked snippet
            blockedSnippetSelector = SelectorUtil.attr(ExtNodeConstants.SNIPPET_NODE_TAG_SELECTOR,
                    ExtNodeConstants.SNIPPET_NODE_ATTR_BLOCK, oldRef);
            blockedSnippets = new ArrayList<>(elem.select(blockedSnippetSelector));

            // find blocked embed
            blockedSnippetSelector = SelectorUtil.attr(ExtNodeConstants.EMBED_NODE_TAG_SELECTOR, ExtNodeConstants.SNIPPET_NODE_ATTR_BLOCK,
                    oldRef);
            blockedSnippets.addAll(elem.select(blockedSnippetSelector));

            for (Element be : blockedSnippets) {
                be.attr(ExtNodeConstants.SNIPPET_NODE_ATTR_BLOCK, newRef);
            }
            element.attr(ExtNodeConstants.ATTR_SNIPPET_REF, newRef);
        }
    }

    public final static Element getEmbedNodeContent(Element elem) throws TemplateException {
        String target;
        Configuration conf = Configuration.getConfiguration();
        TemplateResolver templateResolver = conf.getTemplateResolver();
        target = elem.attr(ExtNodeConstants.EMBED_NODE_ATTR_TARGET);
        if (target == null || target.isEmpty()) {
            String message = "Target not defined[" + elem.toString() + "]";
            throw new TemplateException(message);
        }
        Template embedTarget;
        try {
            embedTarget = templateResolver.findTemplate(target);
        } catch (TemplateNotFoundException e) {
            throw new TemplateException(e);
        }

        // TODO all of the following process should be merged into template
        // analyze process and be cached.
        Document embedDoc = embedTarget.getDocumentClone();
        /*
                Elements children = embedDoc.body().children();
                Element wrappingNode = ElementUtil.wrapElementsToSingleNode(children);
        */
        Element wrappingNode = new GroupNode(ExtNodeConstants.GROUP_NODE_ATTR_TYPE_EMBED_WRAPPER);
        // retrieve all the blocks that misincluded into head
        Element head = embedDoc.head();
        Elements headChildren = head.children();
        for (Element child : headChildren) {
            if (StringUtil.in(child.tagName(), "script", "link", ExtNodeConstants.BLOCK_NODE_TAG)) {
                child.remove();
                wrappingNode.appendChild(child);
            }
        }

        Element body = embedDoc.body();
        Elements bodyChildren = body.children();
        wrappingNode.insertChildren(-1, bodyChildren);

        // copy all the attrs to the wrapping group node
        Iterator attrs = elem.attributes().iterator();
        Attribute attr;
        while (attrs.hasNext()) {
            attr = attrs.next();
            wrappingNode.attr(attr.getKey(), attr.getValue());
        }

        // a embed template file may by included many times in same parent
        // template, so we have to avoid duplicated snippet refs
        resetSnippetRefs(wrappingNode);

        return wrappingNode;
    }

    public final static void mergeBlock(Document doc, Element content) {
        Iterator blockIterator = content.select(ExtNodeConstants.BLOCK_NODE_TAG_SELECTOR).iterator();
        Element block, targetBlock;
        String blockTarget, blockType;
        List childNodes;
        while (blockIterator.hasNext()) {
            block = blockIterator.next();
            if (block.hasAttr(ExtNodeConstants.BLOCK_NODE_ATTR_OVERRIDE)) {
                blockType = ExtNodeConstants.BLOCK_NODE_ATTR_OVERRIDE;
            } else if (block.hasAttr(ExtNodeConstants.BLOCK_NODE_ATTR_APPEND)) {
                blockType = ExtNodeConstants.BLOCK_NODE_ATTR_APPEND;
            } else if (block.hasAttr(ExtNodeConstants.BLOCK_NODE_ATTR_INSERT)) {
                blockType = ExtNodeConstants.BLOCK_NODE_ATTR_INSERT;
            } else if (!block.hasAttr("id")) {
                // TODO I want a approach to logging out template file path here
                logger.warn("The block does not declare its action or id correctlly.[{}]", block.toString());
                continue;
            } else {
                continue;
            }

            blockTarget = block.attr(blockType);
            if (blockTarget == null || blockTarget.isEmpty()) {
                // TODO I want a approach to logging out template file path here
                logger.warn("The block does not declare its target action correctlly.[{}]", block.toString());
                continue;
            }
            targetBlock = doc.select(SelectorUtil.id(ExtNodeConstants.BLOCK_NODE_TAG_SELECTOR, blockTarget)).first();
            if (targetBlock == null) {
                // TODO I want a approach to logging out template file path here
                logger.warn("The block declares a not existed target block.[{}]", block.toString());
                continue;
            }
            childNodes = new ArrayList<>(block.childNodes());
            switch (blockType) {
            case ExtNodeConstants.BLOCK_NODE_ATTR_OVERRIDE:
                targetBlock.empty();
                targetBlock.insertChildren(-1, childNodes);
                break;
            case ExtNodeConstants.BLOCK_NODE_ATTR_APPEND:
                targetBlock.insertChildren(-1, childNodes);
                break;
            case ExtNodeConstants.BLOCK_NODE_ATTR_INSERT:
                targetBlock.insertChildren(0, childNodes);
                break;
            }
            block.remove();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy