org.apache.shindig.gadgets.spec.GadgetSpec.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.shindig.gadgets.spec.GadgetSpec.java

Source

/*
 * 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.
 */
package org.apache.shindig.gadgets.spec;

import org.apache.commons.lang.StringUtils;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.util.HashUtil;
import org.apache.shindig.common.xml.XmlUtil;
import org.apache.shindig.gadgets.variables.Substitutions;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * Represents a gadget specification root element (Module).
 *
 * @see <a href="http://www.opensocial.org/Technical-Resources/opensocial-spec-v08/gadget-spec">gadgets spec</a>
 */
public class GadgetSpec {
    public static final String DEFAULT_VIEW = "default";
    public static final Locale DEFAULT_LOCALE = new Locale("all", "ALL");

    /**
     * Creates a new Module from the given xml input.
     *
     * @param url The original url of the gadget.
     * @param doc The pre-parsed xml document.
     * @param original Unparsed input XML. Used to generate checksums.
     *
     * @throws SpecParserException If xml can not be processed as a valid gadget spec.
     */
    public GadgetSpec(Uri url, Element doc, String original) throws SpecParserException {
        this.url = url;

        // This might not be good enough; should we take message bundle changes into account?
        this.checksum = HashUtil.checksum(original.getBytes());

        NodeList children = doc.getChildNodes();

        ModulePrefs modulePrefs = null;
        ImmutableMap.Builder<String, UserPref> prefsBuilder = ImmutableMap.builder();
        Map<String, List<Element>> views = Maps.newHashMap();
        for (int i = 0, j = children.getLength(); i < j; ++i) {
            Node child = children.item(i);
            if (!(child instanceof Element)) {
                continue;
            }
            Element element = (Element) child;
            String name = element.getTagName();
            if ("ModulePrefs".equals(name)) {
                if (modulePrefs == null) {
                    modulePrefs = new ModulePrefs(element, url);
                } else {
                    throw new SpecParserException("Only 1 ModulePrefs is allowed.");
                }
            }
            if ("UserPref".equals(name)) {
                UserPref pref = new UserPref(element);
                prefsBuilder.put(pref.getName(), pref);
            }
            if ("Content".equals(name)) {
                String viewNames = XmlUtil.getAttribute(element, "view", "default");
                for (String view : StringUtils.split(viewNames, ',')) {
                    view = view.trim();
                    List<Element> viewElements = views.get(view);
                    if (viewElements == null) {
                        viewElements = Lists.newLinkedList();
                        views.put(view, viewElements);
                    }
                    viewElements.add(element);
                }
            }
        }

        if (modulePrefs == null) {
            throw new SpecParserException("At least 1 ModulePrefs is required.");
        } else {
            this.modulePrefs = modulePrefs;
        }

        if (views.isEmpty()) {
            throw new SpecParserException("At least 1 Content is required.");
        } else {
            Map<String, View> tmpViews = Maps.newHashMap();
            for (Map.Entry<String, List<Element>> view : views.entrySet()) {
                View v = new View(view.getKey(), view.getValue(), url);
                tmpViews.put(v.getName(), v);
            }
            this.views = ImmutableMap.copyOf(tmpViews);
        }
        this.userPrefs = prefsBuilder.build();
    }

    /**
     * Use for testing.
     */
    public GadgetSpec(Uri url, String xml) throws SpecParserException {
        this(url, XmlUtil.parseSilent(xml), xml);
    }

    /**
     * Constructs a GadgetSpec for substitute calls.
     * @param spec
     */
    private GadgetSpec(GadgetSpec spec) {
        url = spec.url;
        checksum = spec.checksum;
        attributes.putAll(spec.attributes);
    }

    /**
     * The url for this gadget spec.
     */
    private final Uri url;

    public Uri getUrl() {
        return url;
    }

    /**
     * A checksum of the gadget's content.
     */
    private final String checksum;

    public String getChecksum() {
        return checksum;
    }

    /**
     * ModulePrefs
     */
    private ModulePrefs modulePrefs;

    public ModulePrefs getModulePrefs() {
        return modulePrefs;
    }

    /**
     * UserPref
     */
    private Map<String, UserPref> userPrefs;

    public Map<String, UserPref> getUserPrefs() {
        return userPrefs;
    }

    /**
     * Content
     * Mapping is view -> Content section.
     */
    private Map<String, View> views;

    public Map<String, View> getViews() {
        return views;
    }

    /**
     * Retrieves a single view by name.
     *
     * @param name The name of the view you want to see
     * @return The view object, if it exists, or null.
     */
    public View getView(String name) {
        return views.get(name);
    }

    /**
     * A map of attributes associated with the instance of the spec
     * Used by handler classes to use specs to carry context.
     * Not defined by the specification
     */
    private final Map<String, Object> attributes = new MapMaker().makeMap();

    public Object getAttribute(String key) {
        return attributes.get(key);
    }

    /**
     * Sets an attribute on the gadget spec. This should only be done during a constructing phase, as
     * a GadgetSpec should be effectively immutable after it is constructed.
     *
     * @param key The attribute name.
     * @param o The value of the attribute.
     */
    public void setAttribute(String key, Object o) {
        attributes.put(key, o);
    }

    /**
     * Performs substitutions on the spec. See individual elements for
     * details on what gets substituted.
     *
     * @param substituter
     * @return The substituted spec.
     */
    public GadgetSpec substitute(Substitutions substituter) {
        GadgetSpec spec = new GadgetSpec(this);
        spec.modulePrefs = modulePrefs.substitute(substituter);

        if (userPrefs.isEmpty()) {
            spec.userPrefs = ImmutableMap.of();
        } else {
            ImmutableMap.Builder<String, UserPref> prefs = ImmutableMap.builder();
            for (UserPref pref : this.userPrefs.values()) {
                prefs.put(pref.getName(), pref.substitute(substituter));
            }
            spec.userPrefs = prefs.build();
        }

        ImmutableMap.Builder<String, View> viewMap = ImmutableMap.builder();
        for (View view : views.values()) {
            viewMap.put(view.getName(), view.substitute(substituter));
        }
        spec.views = viewMap.build();

        return spec;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append("<Module>\n").append(modulePrefs).append('\n');
        for (UserPref pref : userPrefs.values()) {
            buf.append(pref).append('\n');
        }
        for (Map.Entry<String, View> view : views.entrySet()) {
            buf.append(view.getValue()).append('\n');
        }
        buf.append("</Module>");
        return buf.toString();
    }
}