org.hippoecm.frontend.editor.layout.XmlLayoutDescriptor.java Source code

Java tutorial

Introduction

Here is the source code for org.hippoecm.frontend.editor.layout.XmlLayoutDescriptor.java

Source

/*
 *  Copyright 2009-2013 Hippo B.V. (http://www.onehippo.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.hippoecm.frontend.editor.layout;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.wicket.Application;
import org.apache.wicket.Session;
import org.apache.wicket.core.util.resource.UrlResourceStream;
import org.apache.wicket.core.util.resource.locator.ResourceNameIterator;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.request.resource.IResource;
import org.apache.wicket.request.resource.ResourceStreamResource;
import org.apache.wicket.resource.IPropertiesFactory;
import org.apache.wicket.resource.Properties;
import org.apache.wicket.util.resource.IResourceStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XmlLayoutDescriptor implements ILayoutDescriptor {

    private static final long serialVersionUID = 1L;

    static final Logger log = LoggerFactory.getLogger(XmlLayoutDescriptor.class);

    class LayoutPad implements ILayoutPad {
        private static final long serialVersionUID = 1L;

        String name;
        Orientation orientation;

        LayoutPad(String name, Orientation orientation) {
            this.name = name;
            this.orientation = orientation;
        }

        public String getName() {
            return name;
        }

        public ILayoutTransition getTransition(String name) {
            for (ILayoutTransition transition : transitions) {
                if (transition.getName().equals(name)) {
                    return transition;
                }
            }
            throw new RuntimeException("Transition " + name + " was not found");
        }

        public List<String> getTransitions() {
            List<String> result = new LinkedList<String>();
            for (LayoutTransition transition : transitions) {
                if (transition.getSource() == this) {
                    result.add(transition.getName());
                }
            }
            return result;
        }

        public Orientation getOrientation() {
            return orientation;
        }

        public boolean isList() {
            return orientation != null;
        }

    }

    static class LayoutTransition implements ILayoutTransition {
        private static final long serialVersionUID = 1L;

        private String name;
        private ILayoutPad source;
        private ILayoutPad target;

        LayoutTransition(String name, ILayoutPad source, ILayoutPad target) {
            this.name = name;
            this.source = source;
            this.target = target;
        }

        public String getName() {
            return name;
        }

        public ILayoutPad getSource() {
            return source;
        }

        public ILayoutPad getTarget() {
            return target;
        }

    }

    private IModel<ClassLoader> clModel;
    private String location;
    private String variant;
    private Map<String, ILayoutPad> pads;
    private List<LayoutTransition> transitions;

    /**
     * Constructor
     * 
     * @param classLoaderModel read-only IModel that returns a ClassLoader
     * @param plugin
     */
    public XmlLayoutDescriptor(IModel<ClassLoader> classLoaderModel, String plugin) {
        this(classLoaderModel, plugin, null);
    }

    public XmlLayoutDescriptor(IModel<ClassLoader> classLoaderModel, String plugin, String variant) {
        this.clModel = classLoaderModel;
        this.location = plugin.replace('.', '/');
        this.variant = variant;

        pads = new LinkedHashMap<String, ILayoutPad>();
        transitions = new LinkedList<LayoutTransition>();

        // Get layout description.  Use the variant description if it is available.
        // Otherwise, fall back to the default. 
        ClassLoader cl = clModel.getObject();
        InputStream stream = null;
        if (cl != null) {
            if (variant != null) {
                stream = cl.getResourceAsStream(location + "_" + variant + ".layout.xml");
            }
            if (stream == null) {
                stream = cl.getResourceAsStream(location + ".layout.xml");
            }
        }
        if (stream == null) {
            log.info("No layout descriptor found for " + location);
            return;
        }

        // parse input stream
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            dbf.setValidating(false);

            DocumentBuilder db = dbf.newDocumentBuilder();
            Document document = db.parse(stream);

            Element element = document.getDocumentElement();
            if (!"layout".equals(element.getNodeName())) {
                throw new RuntimeException("unable to parse layout: no layout node found");
            }

            NodeList nodes;

            nodes = element.getElementsByTagName("pad");
            for (int i = 0; i < nodes.getLength(); i++) {
                Element padElement = (Element) nodes.item(i);
                String padName = padElement.getAttribute("name");
                ILayoutPad.Orientation orientation = null;
                if (padElement.hasAttribute("orientation")) {
                    orientation = ILayoutPad.Orientation.VERTICAL;
                    if ("horizontal".equals(padElement.getAttribute("orientation"))) {
                        orientation = ILayoutPad.Orientation.HORIZONTAL;
                    }
                }
                pads.put(padName, new LayoutPad(padName, orientation));
            }

            nodes = element.getElementsByTagName("transition");
            for (int i = 0; i < nodes.getLength(); i++) {
                Element transitionElement = (Element) nodes.item(i);
                String name = transitionElement.getAttribute("name");
                String from = transitionElement.getAttribute("source");
                String to = transitionElement.getAttribute("target");
                transitions.add(new LayoutTransition(name, pads.get(from), pads.get(to)));
            }

        } catch (ParserConfigurationException ex) {
            throw new RuntimeException("Parser configuration error:", ex);
        } catch (SAXException ex) {
            throw new RuntimeException("SAX error:", ex);
        } catch (IOException e) {
            throw new RuntimeException("Error reading layout descriptor resource:", e);
        }
    }

    public Map<String, ILayoutPad> getLayoutPads() {
        return pads;
    }

    public IResource getIcon() {
        return new ResourceStreamResource() {
            private static final long serialVersionUID = 1L;

            @Override
            protected IResourceStream getResourceStream() {
                ClassLoader cl = clModel.getObject();
                URL url = null;
                if (variant != null) {
                    url = cl.getResource(location + "_" + variant + ".png");
                }
                if (url == null) {
                    url = cl.getResource(location + ".png");
                }
                if (url != null) {
                    return new UrlResourceStream(url);
                } else {
                    cl = getClass().getClassLoader();
                    return new UrlResourceStream(
                            cl.getResource(getClass().getPackage().getName().replace('.', '/') + "/no-layout.png"));
                }
            }

        };
    }

    public String getPluginClass() {
        return location.replace('/', '.');
    }

    public String getVariant() {
        return variant;
    }

    /**
     * Determine the name by use of properties files.  The lookup procedure uses the
     * variant properties file, if available, falling back to the no-variant 
     * <code>location + ".properties"</code> file.  The key that is used is the name
     * of the layout (i.e. the last part of its location path), initially tried with
     * the variant appended and falling back to the using the name itself as key if
     * this fails.  Finally, if no properties files are found, the name is returned.
     */
    public IModel<String> getName() {
        return new LoadableDetachableModel<String>() {
            private static final long serialVersionUID = 1L;

            @Override
            protected String load() {
                // Load the properties associated with the path
                IPropertiesFactory propertiesFactory = Application.get().getResourceSettings()
                        .getPropertiesFactory();

                Locale locale = Session.get().getLocale();
                String name = location.substring(location.lastIndexOf('/') + 1);
                ResourceNameIterator iterator = new ResourceNameIterator(location, null, variant, locale, null,
                        false);
                while (iterator.hasNext()) {
                    String path = iterator.next();
                    final Properties props = propertiesFactory.load(null, path);
                    if (props != null) {
                        if (variant != null) {
                            // Lookup the value
                            String value = props.getString(name + "_" + variant);
                            if (value != null) {
                                return value;
                            }
                        }
                        String value = props.getString(name);
                        if (value != null) {
                            return value;
                        }
                    }
                }
                return name;
            }

        };
    }

}