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

org.apache.abdera.ext.json.JSONUtil Maven / Gradle / Ivy

There is a newer version: 1.1.3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  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.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.abdera.ext.json;

import java.io.IOException;
import java.io.Writer;
import java.util.List;

import javax.xml.namespace.QName;

import org.apache.abdera.Abdera;
import org.apache.abdera.ext.bidi.BidiHelper;
import org.apache.abdera.ext.history.FeedPagingHelper;
import org.apache.abdera.ext.html.HtmlHelper;
import org.apache.abdera.ext.thread.InReplyTo;
import org.apache.abdera.ext.thread.ThreadHelper;
import org.apache.abdera.i18n.iri.IRI;
import org.apache.abdera.i18n.text.Bidi.Direction;
import org.apache.abdera.model.Base;
import org.apache.abdera.model.Categories;
import org.apache.abdera.model.Category;
import org.apache.abdera.model.Collection;
import org.apache.abdera.model.Content;
import org.apache.abdera.model.Control;
import org.apache.abdera.model.Div;
import org.apache.abdera.model.Document;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.ExtensibleElement;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Generator;
import org.apache.abdera.model.Link;
import org.apache.abdera.model.Person;
import org.apache.abdera.model.Service;
import org.apache.abdera.model.Source;
import org.apache.abdera.model.Text;
import org.apache.abdera.model.TextValue;
import org.apache.abdera.model.Workspace;
import org.apache.abdera.xpath.XPath;

@SuppressWarnings("unchecked")
public class JSONUtil {

    public static void toJson(Base base, Writer writer) throws IOException {
        JSONStream jstream = new JSONStream(writer);
        if (base instanceof Document) {
            toJson((Document)base, jstream);
        } else if (base instanceof Element) {
            toJson((Element)base, jstream);
        }
    }

    private static boolean isSameAsParentBase(Element element) {
        IRI parentbase = null;
        if (element.getParentElement() != null) {
            parentbase =
                element instanceof Document ? ((Document)element).getBaseUri() : ((Element)element)
                    .getResolvedBaseUri();
        }
        IRI base = element.getResolvedBaseUri();

        if (parentbase == null && base != null) {
            return false;
        }
        if (parentbase == null && base == null) {
            return true;
        }
        return parentbase.equals(element.getResolvedBaseUri());
    }

    private static void toJson(Element element, JSONStream jstream) throws IOException {

        if (element instanceof Text) {
            Text text = (Text)element;
            Text.Type texttype = text.getTextType();
            if (texttype.equals(Text.Type.TEXT) && !needToWriteLanguageFields(text)) {
                jstream.writeQuoted(text.getValue());
            } else {
                jstream.startObject();
                jstream.writeField("attributes");
                jstream.startObject();
                jstream.writeField("type", texttype.name().toLowerCase());
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element)) {
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                }
                jstream.endObject();
                jstream.writeField("children");
                switch (text.getTextType()) {
                    case TEXT:
                        jstream.startArray();
                        jstream.writeQuoted(text.getValue());
                        jstream.endArray();
                        break;
                    case HTML:
                        if (text.getValue() != null) {
                            Div div = HtmlHelper.parse(text.getValue());
                            writeElementValue(div, jstream);
                        } else {
                            jstream.writeQuoted("");
                        }
                        break;
                    case XHTML:
                        if (text.getValueElement() != null) {
                            writeElementValue(text.getValueElement(), jstream);
                        } else {
                            jstream.writeQuoted("");
                        }
                        break;
                }
                jstream.endObject();
            }
        } else if (element instanceof Content) {
            Content content = (Content)element;
            Content.Type contenttype = content.getContentType();
            if (contenttype.equals(Content.Type.TEXT) && !needToWriteLanguageFields(content)) {
                jstream.writeQuoted(content.getValue());
            } else {
                jstream.startObject();
                jstream.writeField("attributes");
                jstream.startObject();
                switch (content.getContentType()) {
                    case TEXT:
                    case HTML:
                    case XHTML:
                        jstream.writeField("type", contenttype.name().toLowerCase());
                        break;
                    case MEDIA:
                    case XML:
                        jstream.writeField("type", content.getMimeType());
                }
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                writeLanguageFields(content, jstream);
                jstream.writeField("src", content.getResolvedSrc());
                jstream.endObject();
                jstream.writeField("children");
                switch (content.getContentType()) {
                    case TEXT:
                        jstream.startArray();
                        jstream.writeQuoted(content.getValue());
                        jstream.endArray();
                        break;
                    case HTML:
                        Div div = HtmlHelper.parse(content.getValue());
                        writeElementValue(div, jstream);
                        break;
                    case XHTML:
                        writeElementValue(content.getValueElement(), jstream);
                        break;
                    case MEDIA:
                    case XML:
                        jstream.startArray();
                        jstream.writeQuoted(content.getValue());
                        jstream.endArray();
                }
                jstream.endObject();
            }
        } else {
            if (element instanceof Categories) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Categories categories = (Categories)element;
                jstream.writeField("fixed", categories.isFixed() ? "true" : "false");
                jstream.writeField("scheme", categories.getScheme());
                writeList("categories", categories.getCategories(), jstream);
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Category) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Category category = (Category)element;
                jstream.writeField("term", category.getTerm());
                jstream.writeField("scheme", category.getScheme());
                jstream.writeField("label", category.getLabel());
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Collection) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Collection collection = (Collection)element;
                jstream.writeField("href", collection.getResolvedHref());
                writeElement("title", collection.getTitleElement(), jstream);
                String[] accepts = collection.getAccept();
                if (accepts != null && accepts.length > 0) {
                    jstream.writeField("accept");
                    jstream.startArray();
                    for (int n = 0; n < accepts.length; n++) {
                        jstream.writeQuoted(accepts[n]);
                        if (n < accepts.length - 1)
                            jstream.writeSeparator();
                    }
                    jstream.endArray();
                }
                List cats = collection.getCategories();
                if (cats.size() > 0)
                    writeList("categories", collection.getCategories(), jstream);
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Control) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element)) {
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                }
                Control control = (Control)element;
                jstream.writeField("draft", control.isDraft() ? "true" : "false");
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Entry) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element)){
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                }
                Entry entry = (Entry)element;
                jstream.writeField("id", entry.getId());
                writeElement("title", entry.getTitleElement(), jstream);
                writeElement("summary", entry.getSummaryElement(), jstream);
                writeElement("rights", entry.getRightsElement(), jstream);
                writeElement("content", entry.getContentElement(), jstream);
                jstream.writeField("updated", entry.getUpdated());
                jstream.writeField("published", entry.getPublished());
                jstream.writeField("edited", entry.getEdited());
                writeElement("source", entry.getSource(), jstream);
                writeList("authors", entry.getAuthors(), jstream);
                writeList("contributors", entry.getContributors(), jstream);
                writeList("links", entry.getLinks(), jstream);
                writeList("categories", entry.getCategories(), jstream);
                writeList("inreplyto", ThreadHelper.getInReplyTos(entry), jstream);
                writeElement("control", entry.getControl(), jstream);
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Generator) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element)) {
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                }
                Generator generator = (Generator)element;
                jstream.writeField("version", generator.getVersion());
                jstream.writeField("uri", generator.getResolvedUri());
                jstream.writeField("value", generator.getText());
                jstream.endObject();
            } else if (element instanceof Link) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element)) {
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                }
                Link link = (Link)element;
                jstream.writeField("href", link.getResolvedHref());
                jstream.writeField("rel", link.getRel());
                jstream.writeField("title", link.getTitle());
                jstream.writeField("type", link.getMimeType());
                jstream.writeField("hreflang", link.getHrefLang());
                if (link.getLength() > -1) {
                    jstream.writeField("length", link.getLength());
                }
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Person) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Person person = (Person)element;
                jstream.writeField("name", person.getName());
                if (person.getEmail() != null)
                    jstream.writeField("email", person.getEmail());
                if (person.getUri() != null)
                    jstream.writeField("uri", person.getUriElement().getResolvedValue());
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Service) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Service service = (Service)element;
                writeList("workspaces", service.getWorkspaces(), jstream);
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Source) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Source source = (Source)element;
                jstream.writeField("id", source.getId());
                writeElement("title", source.getTitleElement(), jstream);
                writeElement("subtitle", source.getSubtitleElement(), jstream);
                writeElement("rights", source.getRightsElement(), jstream);
                jstream.writeField("updated", source.getUpdated());
                writeElement("generator", source.getGenerator(), jstream);
                if (source.getIconElement() != null)
                    jstream.writeField("icon", source.getIconElement().getResolvedValue());
                if (source.getLogoElement() != null)
                    jstream.writeField("logo", source.getLogoElement().getResolvedValue());
                writeList("authors", source.getAuthors(), jstream);
                writeList("contributors", source.getContributors(), jstream);
                writeList("links", source.getLinks(), jstream);
                writeList("categories", source.getCategories(), jstream);
                if (FeedPagingHelper.isComplete(source))
                    jstream.writeField("complete", true);
                if (FeedPagingHelper.isArchive(source))
                    jstream.writeField("archive", true);
                if (source instanceof Feed) {
                    writeList("entries", ((Feed)source).getEntries(), jstream);
                }
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof Workspace) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                Workspace workspace = (Workspace)element;
                writeElement("title", workspace.getTitleElement(), jstream);
                writeList("collections", workspace.getCollections(), jstream);
                writeExtensions((ExtensibleElement)element, jstream);
                jstream.endObject();
            } else if (element instanceof InReplyTo) {
                jstream.startObject();
                writeLanguageFields(element, jstream);
                if (!isSameAsParentBase(element))
                    jstream.writeField("xml:base", element.getResolvedBaseUri());
                InReplyTo irt = (InReplyTo)element;
                jstream.writeField("ref", irt.getRef());
                jstream.writeField("href", irt.getResolvedHref());
                jstream.writeField("type", irt.getMimeType());
                jstream.writeField("source", irt.getResolvedSource());
                jstream.endObject();
            } else {
                writeElement(element, null, jstream);
            }
        }
    }

    private static void writeElementValue(Element element, JSONStream jstream) throws IOException {
        writeElementChildren(element, jstream);
    }

    private static String getName(QName qname) {
        String prefix = qname.getPrefix();
        String name = qname.getLocalPart();
        return (prefix != null && !"".equals(prefix)) ? prefix + ":" + name : name;
    }

    private static void writeElement(Element child, QName parentqname, JSONStream jstream) throws IOException {
        QName childqname = child.getQName();
        String prefix = childqname.getPrefix();
        jstream.startObject();
        jstream.writeField("name", getName(childqname));
        jstream.writeField("attributes");
        List attributes = child.getAttributes();
        jstream.startObject();
        if (!isSameNamespace(childqname, parentqname)) {
            if (prefix != null && !"".equals(prefix))
                jstream.writeField("xmlns:" + prefix);
            else
                jstream.writeField("xmlns");
            jstream.writeQuoted(childqname.getNamespaceURI());
        }
        if (!isSameAsParentBase(child))
            jstream.writeField("xml:base", child.getResolvedBaseUri());
        writeLanguageFields(child, jstream);
        for (QName attr : attributes) {
            String name = getName(attr);
            jstream.writeField(name);
            if ("".equals(attr.getPrefix()) || "xml".equals(attr.getPrefix())) {
                String val = child.getAttributeValue(attr);
                if (val != null && ("href".equalsIgnoreCase(name) || "src".equalsIgnoreCase(name) || "action"
                    .equalsIgnoreCase(name))) {
                    IRI base = child.getResolvedBaseUri();
                    if (base != null)
                        val = base.resolve(val).toASCIIString();
                }
                jstream.writeQuoted(val);
            } else {
                jstream.startObject();
                jstream.writeField("attributes");
                jstream.startObject();
                jstream.writeField("xmlns:" + attr.getPrefix());
                jstream.writeQuoted(attr.getNamespaceURI());
                jstream.endObject();
                jstream.writeField("value");
                jstream.writeQuoted(child.getAttributeValue(attr));
                jstream.endObject();
            }
        }
        jstream.endObject();
        jstream.writeField("children");
        writeElementChildren((Element)child, jstream);
        jstream.endObject();
    }

    private static void writeElementChildren(Element element, JSONStream jstream) throws IOException {
        jstream.startArray();
        Object[] children = getChildren(element);
        QName parentqname = element.getQName();
        for (int n = 0; n < children.length; n++) {
            Object child = children[n];
            if (child instanceof Element) {
                writeElement((Element)child, parentqname, jstream);
                if (n < children.length - 1)
                    jstream.writeSeparator();
            } else if (child instanceof TextValue) {
                TextValue textvalue = (TextValue)child;
                String value = textvalue.getText();
                if (!element.getMustPreserveWhitespace()) {
                    if (!value.matches("\\s*")) {
                        jstream.writeQuoted(value.trim());
                        if (n < children.length - 1)
                            jstream.writeSeparator();
                    }
                } else {
                    jstream.writeQuoted(value);
                    if (n < children.length - 1)
                        jstream.writeSeparator();
                }
            }
        }
        jstream.endArray();
    }

    private static void writeExtensions(ExtensibleElement element, JSONStream jstream) throws IOException {
        writeExtensions(element, jstream, true);
    }

    private static void writeExtensions(ExtensibleElement element, JSONStream jstream, boolean startsep)
        throws IOException {
        List attributes = element.getExtensionAttributes();
        writeList("extensions", element.getExtensions(), jstream);
        if (attributes.size() > 0) {
            jstream.writeField("attributes");

            jstream.startObject();
            for (int n = 0; n < attributes.size(); n++) {
                QName qname = attributes.get(n);
                jstream.writeField(getName(qname));
                if ("".equals(qname.getPrefix()) || "xml".equals(qname.getPrefix())) {
                    jstream.writeQuoted(element.getAttributeValue(qname));
                } else {
                    jstream.startObject();
                    jstream.writeField("attributes");
                    jstream.startObject();
                    jstream.writeField("xmlns:" + qname.getPrefix());
                    jstream.writeQuoted(qname.getNamespaceURI());
                    jstream.endObject();
                    jstream.writeField("value");
                    jstream.writeQuoted(element.getAttributeValue(qname));
                    jstream.endObject();
                }
            }
            jstream.endObject();
        }
    }

    private static boolean needToWriteLanguageFields(Element element) {
        return needToWriteLang(element) || needToWriteDir(element);
    }

    private static boolean needToWriteLang(Element element) {
        String parentlang = null;
        if (element.getParentElement() != null) {
            Base parent = element.getParentElement();
            parentlang =
                parent instanceof Document ? ((Document)parent).getLanguage() : ((Element)parent).getLanguage();
        }
        String lang = element.getLanguage();
        return (parentlang == null && lang != null) || (lang != null && parentlang != null && !parentlang
            .equalsIgnoreCase(lang));
    }

    private static boolean needToWriteDir(Element element) {
        Direction parentdir = Direction.UNSPECIFIED;
        Direction dir = BidiHelper.getDirection(element);
        if (element.getParentElement() != null) {
            Base parent = element.getParentElement();
            if (parent instanceof Element)
                parentdir = BidiHelper.getDirection((Element)parent);
        }
        return dir != Direction.UNSPECIFIED && !dir.equals(parentdir);
    }

    private static void writeLanguageFields(Element element, JSONStream jstream) throws IOException {
        if (needToWriteLang(element)) {
            String lang = element.getLanguage();
            jstream.writeField("lang", lang);
        }
        if (needToWriteDir(element)) {
            Direction dir = BidiHelper.getDirection(element);
            jstream.writeField("dir", dir.name().toLowerCase());
        }
    }

    private static void writeElement(String name, Element element, JSONStream jstream) throws IOException {
        if (element != null) {
            jstream.writeField(name);
            toJson(element, jstream);
        }
    }

    private static boolean writeList(String name, List list, JSONStream jstream) throws IOException {
        if (list == null || list.size() == 0)
            return false;
        jstream.writeField(name);
        jstream.startArray();
        for (int n = 0; n < list.size(); n++) {
            Element el = (Element)list.get(n);
            if (!(el instanceof InReplyTo) && !(el instanceof Control)) {
                toJson(el, jstream);
                if (n < list.size() - 1)
                    jstream.writeSeparator();
            }
        }
        jstream.endArray();
        return true;
    }

    private static void toJson(Document document, JSONStream jstream) throws IOException {
        jstream.startObject();
        jstream.writeField("base", document.getBaseUri());
        jstream.writeField("content-type", document.getContentType());
        jstream.writeField("etag", document.getEntityTag());
        jstream.writeField("language", document.getLanguage());
        jstream.writeField("slug", document.getSlug());
        jstream.writeField("last-modified", document.getLastModified());

        Element root = document.getRoot();
        if (root != null) {
            String rootname = root.getQName().getLocalPart();
            writeElement(rootname, document.getRoot(), jstream);
        }
        jstream.endObject();
    }

    private static Object[] getChildren(Element element) {
        Abdera abdera = element.getFactory().getAbdera();
        XPath xpath = abdera.getXPath();
        List nodes = xpath.selectNodes("node()", element);
        return nodes.toArray(new Object[nodes.size()]);
    }

    private static boolean isSameNamespace(QName q1, QName q2) {
        if (q1 == null && q2 != null)
            return false;
        if (q1 != null && q2 == null)
            return false;
        String p1 = q1 == null ? "" : q1.getPrefix() != null ? q1.getPrefix() : "";
        String p2 = q2 == null ? "" : q2.getPrefix() != null ? q2.getPrefix() : "";
        String n1 = q1 == null ? "" : q1.getNamespaceURI() != null ? q1.getNamespaceURI() : "";
        String n2 = q2 == null ? "" : q2.getNamespaceURI() != null ? q2.getNamespaceURI() : "";
        return n1.equals(n2) && p1.equals(p2);
    }

}