org.xwiki.rest.internal.ModelFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.rest.internal.ModelFactory.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.rest.internal;

import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Formatter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang3.StringUtils;
import org.xwiki.component.annotation.Component;
import org.xwiki.job.DefaultRequest;
import org.xwiki.job.Request;
import org.xwiki.logging.LogLevel;
import org.xwiki.logging.LogQueue;
import org.xwiki.logging.event.LogEvent;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.model.reference.ObjectReference;
import org.xwiki.rest.Relations;
import org.xwiki.rest.XWikiRestException;
import org.xwiki.rest.model.jaxb.Attachment;
import org.xwiki.rest.model.jaxb.Attribute;
import org.xwiki.rest.model.jaxb.Class;
import org.xwiki.rest.model.jaxb.JobId;
import org.xwiki.rest.model.jaxb.JobLog;
import org.xwiki.rest.model.jaxb.JobProgress;
import org.xwiki.rest.model.jaxb.JobRequest;
import org.xwiki.rest.model.jaxb.JobStatus;
import org.xwiki.rest.model.jaxb.Link;
import org.xwiki.rest.model.jaxb.MapEntry;
import org.xwiki.rest.model.jaxb.Object;
import org.xwiki.rest.model.jaxb.ObjectFactory;
import org.xwiki.rest.model.jaxb.ObjectSummary;
import org.xwiki.rest.model.jaxb.Page;
import org.xwiki.rest.model.jaxb.PageSummary;
import org.xwiki.rest.model.jaxb.Property;
import org.xwiki.rest.model.jaxb.Space;
import org.xwiki.rest.model.jaxb.Translation;
import org.xwiki.rest.model.jaxb.Translations;
import org.xwiki.rest.model.jaxb.Wiki;
import org.xwiki.rest.resources.ModificationsResource;
import org.xwiki.rest.resources.SyntaxesResource;
import org.xwiki.rest.resources.attachments.AttachmentResource;
import org.xwiki.rest.resources.attachments.AttachmentVersionResource;
import org.xwiki.rest.resources.attachments.AttachmentsAtPageVersionResource;
import org.xwiki.rest.resources.attachments.AttachmentsResource;
import org.xwiki.rest.resources.classes.ClassPropertiesResource;
import org.xwiki.rest.resources.classes.ClassPropertyResource;
import org.xwiki.rest.resources.classes.ClassResource;
import org.xwiki.rest.resources.classes.ClassesResource;
import org.xwiki.rest.resources.comments.CommentsResource;
import org.xwiki.rest.resources.comments.CommentsVersionResource;
import org.xwiki.rest.resources.objects.AllObjectsForClassNameResource;
import org.xwiki.rest.resources.objects.ObjectAtPageVersionResource;
import org.xwiki.rest.resources.objects.ObjectPropertiesAtPageVersionResource;
import org.xwiki.rest.resources.objects.ObjectPropertiesResource;
import org.xwiki.rest.resources.objects.ObjectPropertyAtPageVersionResource;
import org.xwiki.rest.resources.objects.ObjectPropertyResource;
import org.xwiki.rest.resources.objects.ObjectResource;
import org.xwiki.rest.resources.objects.ObjectsAtPageVersionResource;
import org.xwiki.rest.resources.objects.ObjectsResource;
import org.xwiki.rest.resources.pages.PageChildrenResource;
import org.xwiki.rest.resources.pages.PageHistoryResource;
import org.xwiki.rest.resources.pages.PageResource;
import org.xwiki.rest.resources.pages.PageTagsResource;
import org.xwiki.rest.resources.pages.PageTranslationHistoryResource;
import org.xwiki.rest.resources.pages.PageTranslationResource;
import org.xwiki.rest.resources.pages.PagesResource;
import org.xwiki.rest.resources.spaces.SpaceResource;
import org.xwiki.rest.resources.spaces.SpaceSearchResource;
import org.xwiki.rest.resources.spaces.SpacesResource;
import org.xwiki.rest.resources.wikis.WikiSearchQueryResource;
import org.xwiki.rest.resources.wikis.WikiSearchResource;

import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.api.Document;
import com.xpn.xwiki.api.PropertyClass;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;
import com.xpn.xwiki.objects.BaseProperty;
import com.xpn.xwiki.objects.PropertyInterface;
import com.xpn.xwiki.objects.classes.BaseClass;
import com.xpn.xwiki.objects.classes.ListClass;

/**
 * Various common tools for resources.
 * 
 * @version $Id: 6d3b74b1bf37e9131f1ca4667b78324c708880cd $
 * @since 7.3M1
 */
@Component(roles = ModelFactory.class)
@Singleton
public class ModelFactory {
    private final ObjectFactory objectFactory;

    @Inject
    private Provider<XWikiContext> xcontextProvider;

    @Inject
    private EntityReferenceSerializer<String> serializer;

    @Inject
    private JAXBConverter jaxbConverter;

    public ModelFactory() {
        this.objectFactory = new ObjectFactory();
    }

    public boolean toDocument(Document doc, org.xwiki.rest.model.jaxb.Page restPage) throws XWikiException {
        boolean modified = false;

        if (restPage.getContent() != null) {
            doc.setContent(restPage.getContent());
            modified = true;
        }

        if (restPage.getTitle() != null) {
            doc.setTitle(restPage.getTitle());
            modified = true;
        }

        if (restPage.getParent() != null) {
            doc.setParent(restPage.getParent());
            modified = true;
        }

        if (restPage.getSyntax() != null) {
            doc.setSyntaxId(restPage.getSyntax());
            modified = true;
        }

        doc.setHidden(restPage.isHidden());

        // Set objects
        if (restPage.getObjects() != null) {
            Set<ObjectReference> newReferences = new HashSet<>();

            // Add/update objects
            for (ObjectSummary restObjectSummary : restPage.getObjects().getObjectSummaries()) {
                if (restObjectSummary != null) {
                    org.xwiki.rest.model.jaxb.Object restObject = (org.xwiki.rest.model.jaxb.Object) restObjectSummary;
                    com.xpn.xwiki.api.Object xwikiObject = doc.getObject(restObject.getClassName(),
                            restObject.getNumber());
                    if (xwikiObject == null) {
                        xwikiObject = doc.newObject(restObject.getClassName());
                    }
                    toObject(xwikiObject, restObject);
                    modified = true;

                    newReferences.add(xwikiObject.getReference());
                }
            }

            // Remove objects
            List<com.xpn.xwiki.api.Object> toRemove = new ArrayList<>();
            for (Vector<com.xpn.xwiki.api.Object> objects : doc.getxWikiObjects().values()) {
                for (com.xpn.xwiki.api.Object object : objects) {
                    if (!newReferences.contains(object.getReference())) {
                        toRemove.add(object);
                    }
                }
            }
            for (com.xpn.xwiki.api.Object obj : toRemove) {
                doc.removeObject(obj);
                modified = true;
            }
        }

        // TODO: add support for class, see https://jira.xwiki.org/browse/XWIKI-12597

        // TODO: add attachment content to the REST model ?

        return modified;
    }

    private void fillObjectSummary(ObjectSummary objectSummary, Document doc, BaseObject xwikiObject,
            Boolean withPrettyNames) {
        objectSummary.setId(String.format("%s:%s", doc.getPrefixedFullName(), xwikiObject.getGuid()));
        objectSummary.setGuid(xwikiObject.getGuid());
        objectSummary.setPageId(doc.getPrefixedFullName());
        objectSummary.setPageVersion(doc.getVersion());
        objectSummary.setPageAuthor(doc.getAuthor());
        if (withPrettyNames) {
            XWikiContext xwikiContext = this.xcontextProvider.get();
            objectSummary.setPageAuthorName(
                    xwikiContext.getWiki().getUserName(doc.getAuthor(), null, false, xwikiContext));
        }
        objectSummary.setWiki(doc.getWiki());
        objectSummary.setSpace(doc.getSpace());
        objectSummary.setPageName(doc.getName());
        objectSummary.setClassName(xwikiObject.getClassName());
        objectSummary.setNumber(xwikiObject.getNumber());

        String[] propertyNames = xwikiObject.getPropertyNames();
        if (propertyNames.length > 0) {
            try {
                objectSummary.setHeadline(serializePropertyValue(xwikiObject.get(propertyNames[0])));
            } catch (XWikiException e) {
                // Should never happen
            }
        }
    }

    public ObjectSummary toRestObjectSummary(URI baseUri, Document doc, BaseObject xwikiObject, boolean useVersion,
            Boolean withPrettyNames) {
        ObjectSummary objectSummary = objectFactory.createObjectSummary();
        fillObjectSummary(objectSummary, doc, xwikiObject, withPrettyNames);

        Link objectLink = getObjectLink(objectFactory, baseUri, doc, xwikiObject, useVersion, Relations.OBJECT);
        objectSummary.getLinks().add(objectLink);

        String propertiesUri;
        if (useVersion) {
            propertiesUri = Utils.createURI(baseUri, ObjectPropertiesAtPageVersionResource.class, doc.getWiki(),
                    Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), doc.getVersion(),
                    xwikiObject.getClassName(), xwikiObject.getNumber()).toString();
        } else {
            propertiesUri = Utils.createURI(baseUri, ObjectPropertiesResource.class, doc.getWiki(),
                    Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), xwikiObject.getClassName(),
                    xwikiObject.getNumber()).toString();
        }

        Link propertyLink = objectFactory.createLink();
        propertyLink.setHref(propertiesUri);
        propertyLink.setRel(Relations.PROPERTIES);
        objectSummary.getLinks().add(propertyLink);

        return objectSummary;
    }

    public void toObject(com.xpn.xwiki.api.Object xwikiObject, org.xwiki.rest.model.jaxb.Object restObject) {
        for (Property restProperty : restObject.getProperties()) {
            xwikiObject.set(restProperty.getName(), restProperty.getValue());
        }
    }

    public Object toRestObject(URI baseUri, Document doc, BaseObject xwikiObject, boolean useVersion,
            Boolean withPrettyNames) {
        Object object = this.objectFactory.createObject();
        fillObjectSummary(object, doc, xwikiObject, withPrettyNames);

        XWikiContext xwikiContext = this.xcontextProvider.get();
        BaseClass xwikiClass = xwikiObject.getXClass(xwikiContext);

        for (java.lang.Object propertyClassObject : xwikiClass.getProperties()) {
            com.xpn.xwiki.objects.classes.PropertyClass propertyClass = (com.xpn.xwiki.objects.classes.PropertyClass) propertyClassObject;

            Property property = this.objectFactory.createProperty();

            for (java.lang.Object o : propertyClass.getProperties()) {
                BaseProperty baseProperty = (BaseProperty) o;
                Attribute attribute = this.objectFactory.createAttribute();
                attribute.setName(baseProperty.getName());

                /* Check for null values in order to prevent NPEs */
                if (baseProperty.getValue() != null) {
                    attribute.setValue(baseProperty.getValue().toString());
                } else {
                    attribute.setValue("");
                }

                property.getAttributes().add(attribute);
            }

            if (propertyClass instanceof ListClass) {
                ListClass listClass = (ListClass) propertyClass;

                List allowedValueList = listClass.getList(xwikiContext);

                if (!allowedValueList.isEmpty()) {
                    Formatter f = new Formatter();
                    for (int i = 0; i < allowedValueList.size(); i++) {
                        if (i != allowedValueList.size() - 1) {
                            f.format("%s,", allowedValueList.get(i).toString());
                        } else {
                            f.format("%s", allowedValueList.get(i).toString());
                        }
                    }

                    Attribute attribute = this.objectFactory.createAttribute();
                    attribute.setName(Constants.ALLOWED_VALUES_ATTRIBUTE_NAME);
                    attribute.setValue(f.toString());
                    property.getAttributes().add(attribute);
                }
            }

            property.setName(propertyClass.getName());
            property.setType(propertyClass.getClassType());
            try {
                property.setValue(serializePropertyValue(xwikiObject.get(propertyClass.getName())));
            } catch (XWikiException e) {
                // Should never happen
            }

            String propertyUri;

            if (useVersion) {
                propertyUri = Utils
                        .createURI(baseUri, ObjectPropertyAtPageVersionResource.class, doc.getWiki(),
                                Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), doc.getVersion(),
                                xwikiObject.getClassName(), xwikiObject.getNumber(), propertyClass.getName())
                        .toString();
            } else {
                propertyUri = Utils
                        .createURI(baseUri, ObjectPropertyResource.class, doc.getWiki(),
                                Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(),
                                xwikiObject.getClassName(), xwikiObject.getNumber(), propertyClass.getName())
                        .toString();
            }
            Link propertyLink = this.objectFactory.createLink();
            propertyLink.setHref(propertyUri);
            propertyLink.setRel(Relations.SELF);
            property.getLinks().add(propertyLink);

            object.getProperties().add(property);
        }

        Link objectLink = getObjectLink(this.objectFactory, baseUri, doc, xwikiObject, useVersion, Relations.SELF);
        object.getLinks().add(objectLink);

        return object;
    }

    private static Link getObjectLink(ObjectFactory objectFactory, URI baseUri, Document doc,
            BaseObject xwikiObject, boolean useVersion, String relation) {
        String objectUri;

        if (useVersion) {
            objectUri = Utils.createURI(baseUri, ObjectAtPageVersionResource.class, doc.getWiki(),
                    Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), doc.getVersion(),
                    xwikiObject.getClassName(), xwikiObject.getNumber()).toString();
        } else {
            objectUri = Utils.createURI(baseUri, ObjectResource.class, doc.getWiki(),
                    Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), xwikiObject.getClassName(),
                    xwikiObject.getNumber()).toString();
        }
        Link objectLink = objectFactory.createLink();
        objectLink.setHref(objectUri);
        objectLink.setRel(relation);

        return objectLink;
    }

    // To REST

    public Wiki toRestWiki(URI baseUri, String wikiName) {
        Wiki wiki = this.objectFactory.createWiki().withId(wikiName).withName(wikiName);

        String spacesUri = Utils.createURI(baseUri, SpacesResource.class, wikiName).toString();
        Link spacesLink = this.objectFactory.createLink();
        spacesLink.setHref(spacesUri);
        spacesLink.setRel(Relations.SPACES);
        wiki.getLinks().add(spacesLink);

        String classesUri = Utils.createURI(baseUri, ClassesResource.class, wikiName).toString();
        Link classesLink = this.objectFactory.createLink();
        classesLink.setHref(classesUri);
        classesLink.setRel(Relations.CLASSES);
        wiki.getLinks().add(classesLink);

        String modificationsUri = Utils.createURI(baseUri, ModificationsResource.class, wikiName).toString();
        Link modificationsLink = this.objectFactory.createLink();
        modificationsLink.setHref(modificationsUri);
        modificationsLink.setRel(Relations.MODIFICATIONS);
        wiki.getLinks().add(modificationsLink);

        String searchUri = Utils.createURI(baseUri, WikiSearchResource.class, wikiName).toString();
        Link searchLink = this.objectFactory.createLink();
        searchLink.setHref(searchUri);
        searchLink.setRel(Relations.SEARCH);
        wiki.getLinks().add(searchLink);

        String queryUri = Utils.createURI(baseUri, WikiSearchQueryResource.class, wikiName).toString();
        Link queryLink = this.objectFactory.createLink();
        queryLink.setHref(queryUri);
        queryLink.setRel(Relations.QUERY);
        wiki.getLinks().add(queryLink);

        return wiki;
    }

    public Space toRestSpace(URI baseUri, String wikiName, List<String> spaces, Document home) {
        Space space = this.objectFactory.createSpace();
        space.setId(Utils.getSpaceId(wikiName, spaces));
        space.setWiki(wikiName);
        // the name of the space is the last element of the spaces list
        space.setName(spaces.get(spaces.size() - 1));
        if (home != null) {
            space.setHome(home.getPrefixedFullName());
            space.setXwikiRelativeUrl(home.getURL("view"));
            space.setXwikiAbsoluteUrl(home.getExternalURL("view"));
        }

        String pagesUri = Utils.createURI(baseUri, PagesResource.class, wikiName, spaces).toString();
        Link pagesLink = this.objectFactory.createLink();
        pagesLink.setHref(pagesUri);
        pagesLink.setRel(Relations.PAGES);
        space.getLinks().add(pagesLink);

        if (home != null) {
            String homeUri = Utils.createURI(baseUri, PageResource.class, wikiName, spaces, home.getName())
                    .toString();
            Link homeLink = this.objectFactory.createLink();
            homeLink.setHref(homeUri);
            homeLink.setRel(Relations.HOME);
            space.getLinks().add(homeLink);
        }

        String searchUri = Utils.createURI(baseUri, SpaceSearchResource.class, wikiName, spaces).toString();
        Link searchLink = this.objectFactory.createLink();
        searchLink.setHref(searchUri);
        searchLink.setRel(Relations.SEARCH);
        space.getLinks().add(searchLink);

        return space;
    }

    public Translations toRestTranslations(URI baseUri, Document doc) throws XWikiException {
        Translations translations = this.objectFactory.createTranslations();

        List<String> languages = doc.getTranslationList();

        List<String> spaces = Utils.getSpacesFromSpaceId(doc.getSpace());

        if (!languages.isEmpty()) {
            if (!doc.getDefaultLanguage().equals("")) {
                translations.setDefault(doc.getDefaultLanguage());

                Translation translation = this.objectFactory.createTranslation();
                translation.setLanguage(doc.getDefaultLanguage());

                /* Add the default page with the default translation explicitely */
                String pageTranslationUri = Utils
                        .createURI(baseUri, PageResource.class, doc.getWiki(), spaces, doc.getName()).toString();
                Link pageTranslationLink = this.objectFactory.createLink();
                pageTranslationLink.setHref(pageTranslationUri);
                pageTranslationLink.setRel(Relations.PAGE);
                translation.getLinks().add(pageTranslationLink);

                String historyUri = Utils
                        .createURI(baseUri, PageHistoryResource.class, doc.getWiki(), spaces, doc.getName())
                        .toString();
                Link historyLink = this.objectFactory.createLink();
                historyLink.setHref(historyUri);
                historyLink.setRel(Relations.HISTORY);
                translation.getLinks().add(historyLink);

                translations.getTranslations().add(translation);
            }
        }

        for (String language : languages) {
            Translation translation = this.objectFactory.createTranslation();
            translation.setLanguage(language);

            String pageTranslationUri = Utils.createURI(baseUri, PageTranslationResource.class, doc.getWiki(),
                    spaces, doc.getName(), language).toString();
            Link pageTranslationLink = this.objectFactory.createLink();
            pageTranslationLink.setHref(pageTranslationUri);
            pageTranslationLink.setRel(Relations.PAGE);
            translation.getLinks().add(pageTranslationLink);

            String historyUri = Utils.createURI(baseUri, PageTranslationHistoryResource.class, doc.getWiki(),
                    spaces, doc.getName(), language).toString();
            Link historyLink = this.objectFactory.createLink();
            historyLink.setHref(historyUri);
            historyLink.setRel(Relations.HISTORY);
            translation.getLinks().add(historyLink);

            translations.getTranslations().add(translation);
        }

        return translations;
    }

    /**
     * This method is used to fill the "common part" of a Page and a PageSummary.
     */
    private void toRestPageSummary(PageSummary pageSummary, URI baseUri, Document doc, boolean useVersion,
            Boolean withPrettyNames) throws XWikiException {
        XWikiContext xwikiContext = this.xcontextProvider.get();

        pageSummary.setWiki(doc.getWiki());
        pageSummary.setFullName(doc.getFullName());
        pageSummary.setId(doc.getPrefixedFullName());
        pageSummary.setSpace(doc.getSpace());
        pageSummary.setName(doc.getName());
        pageSummary.setTitle(doc.getDisplayTitle());
        pageSummary.setXwikiRelativeUrl(doc.getURL("view"));
        pageSummary.setXwikiAbsoluteUrl(doc.getExternalURL("view"));
        pageSummary.setTranslations(toRestTranslations(baseUri, doc));
        pageSummary.setSyntax(doc.getSyntax().toIdString());
        pageSummary.setVersion(doc.getVersion());
        pageSummary.setAuthor(doc.getAuthor());
        if (withPrettyNames) {
            pageSummary
                    .setAuthorName(xwikiContext.getWiki().getUserName(doc.getAuthor(), null, false, xwikiContext));
        }

        pageSummary.setParent(doc.getParent());
        DocumentReference parentReference = doc.getParentReference();
        boolean parentExist = parentReference != null
                && xwikiContext.getWiki().exists(parentReference, xwikiContext);
        // parentId must not be set if the parent document does not exist.
        if (parentExist) {
            pageSummary.setParentId(this.serializer.serialize(parentReference));
        } else {
            pageSummary.setParentId("");
        }

        List<String> spaces = Utils.getSpacesFromSpaceId(doc.getSpace());
        String spaceUri = Utils.createURI(baseUri, SpaceResource.class, doc.getWiki(), spaces).toString();
        Link spaceLink = this.objectFactory.createLink();
        spaceLink.setHref(spaceUri);
        spaceLink.setRel(Relations.SPACE);
        pageSummary.getLinks().add(spaceLink);

        if (parentExist) {
            String parentUri = Utils.createURI(baseUri, PageResource.class,
                    parentReference.getWikiReference().getName(), spaces, parentReference.getName()).toString();
            Link parentLink = this.objectFactory.createLink();
            parentLink.setHref(parentUri);
            parentLink.setRel(Relations.PARENT);
            pageSummary.getLinks().add(parentLink);
        }

        String historyUri = Utils.createURI(baseUri, PageHistoryResource.class, doc.getWiki(),
                Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName()).toString();
        Link historyLink = this.objectFactory.createLink();
        historyLink.setHref(historyUri);
        historyLink.setRel(Relations.HISTORY);
        pageSummary.getLinks().add(historyLink);

        if (!doc.getChildren().isEmpty()) {
            String pageChildrenUri = Utils
                    .createURI(baseUri, PageChildrenResource.class, doc.getWiki(), spaces, doc.getName())
                    .toString();
            Link pageChildrenLink = this.objectFactory.createLink();
            pageChildrenLink.setHref(pageChildrenUri);
            pageChildrenLink.setRel(Relations.CHILDREN);
            pageSummary.getLinks().add(pageChildrenLink);
        }

        if (!doc.getComments().isEmpty()) {
            String commentsUri;
            if (useVersion) {
                commentsUri = Utils.createURI(baseUri, CommentsVersionResource.class, doc.getWiki(), spaces,
                        doc.getName(), doc.getVersion()).toString();
            } else {
                commentsUri = Utils.createURI(baseUri, CommentsResource.class, doc.getWiki(), spaces, doc.getName())
                        .toString();
            }

            Link commentsLink = this.objectFactory.createLink();
            commentsLink.setHref(commentsUri);
            commentsLink.setRel(Relations.COMMENTS);
            pageSummary.getLinks().add(commentsLink);
        }

        if (!doc.getAttachmentList().isEmpty()) {
            String attachmentsUri;
            if (useVersion) {
                attachmentsUri = Utils.createURI(baseUri, AttachmentsAtPageVersionResource.class, doc.getWiki(),
                        spaces, doc.getName(), doc.getVersion()).toString();
            } else {
                attachmentsUri = Utils
                        .createURI(baseUri, AttachmentsResource.class, doc.getWiki(), spaces, doc.getName())
                        .toString();
            }

            Link attachmentsLink = this.objectFactory.createLink();
            attachmentsLink.setHref(attachmentsUri);
            attachmentsLink.setRel(Relations.ATTACHMENTS);
            pageSummary.getLinks().add(attachmentsLink);
        }

        if (!doc.getxWikiObjects().keySet().isEmpty()) {
            String objectsUri;

            if (useVersion) {
                objectsUri = Utils.createURI(baseUri, ObjectsAtPageVersionResource.class, doc.getWiki(), spaces,
                        doc.getName(), doc.getVersion()).toString();
            } else {
                objectsUri = Utils.createURI(baseUri, ObjectsResource.class, doc.getWiki(), spaces, doc.getName())
                        .toString();
            }
            Link objectsLink = this.objectFactory.createLink();
            objectsLink.setHref(objectsUri);
            objectsLink.setRel(Relations.OBJECTS);
            pageSummary.getLinks().add(objectsLink);
        }

        com.xpn.xwiki.api.Object tagsObject = doc.getObject("XWiki.TagClass", 0);
        if (tagsObject != null) {
            if (tagsObject.getProperty("tags") != null) {
                String tagsUri = Utils
                        .createURI(baseUri, PageTagsResource.class, doc.getWiki(), spaces, doc.getName())
                        .toString();
                Link tagsLink = this.objectFactory.createLink();
                tagsLink.setHref(tagsUri);
                tagsLink.setRel(Relations.TAGS);
                pageSummary.getLinks().add(tagsLink);
            }
        }

        String syntaxesUri = Utils.createURI(baseUri, SyntaxesResource.class).toString();
        Link syntaxesLink = this.objectFactory.createLink();
        syntaxesLink.setHref(syntaxesUri);
        syntaxesLink.setRel(Relations.SYNTAXES);
        pageSummary.getLinks().add(syntaxesLink);
    }

    public PageSummary toRestPageSummary(URI baseUri, Document doc, Boolean withPrettyNames) throws XWikiException {
        PageSummary pageSummary = this.objectFactory.createPageSummary();
        toRestPageSummary(pageSummary, baseUri, doc, false, withPrettyNames);

        String pageUri = Utils.createURI(baseUri, PageResource.class, doc.getWiki(),
                Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName()).toString();
        Link pageLink = this.objectFactory.createLink();
        pageLink.setHref(pageUri);
        pageLink.setRel(Relations.PAGE);
        pageSummary.getLinks().add(pageLink);

        return pageSummary;
    }

    public Page toRestPage(URI baseUri, URI self, Document doc, boolean useVersion, Boolean withPrettyNames,
            Boolean withObjects, Boolean withXClass, Boolean withAttachments) throws XWikiException {
        Page page = this.objectFactory.createPage();
        toRestPageSummary(page, baseUri, doc, useVersion, withPrettyNames);

        XWikiContext xwikiContext = this.xcontextProvider.get();

        page.setMajorVersion(doc.getRCSVersion().at(0));
        page.setMinorVersion(doc.getRCSVersion().at(1));
        page.setHidden(doc.isHidden());
        page.setLanguage(doc.getLocale().toString());
        page.setCreator(doc.getCreator());
        if (withPrettyNames) {
            page.setCreatorName(xwikiContext.getWiki().getUserName(doc.getCreator(), null, false, xwikiContext));
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(doc.getCreationDate());
        page.setCreated(calendar);

        page.setModifier(doc.getContentAuthor());
        if (withPrettyNames) {
            page.setModifierName(
                    xwikiContext.getWiki().getUserName(doc.getContentAuthor(), null, false, xwikiContext));
        }

        calendar = Calendar.getInstance();
        calendar.setTime(doc.getContentUpdateDate());
        page.setModified(calendar);

        page.setComment(doc.getComment());
        page.setContent(doc.getContent());

        if (self != null) {
            Link pageLink = this.objectFactory.createLink();
            pageLink.setHref(self.toString());
            pageLink.setRel(Relations.SELF);
            page.getLinks().add(pageLink);
        }

        com.xpn.xwiki.api.Class xwikiClass = doc.getxWikiClass();
        if (xwikiClass != null) {
            String classUri = Utils.createURI(baseUri, ClassResource.class, doc.getWiki(), xwikiClass.getName())
                    .toString();
            Link classLink = this.objectFactory.createLink();
            classLink.setHref(classUri);
            classLink.setRel(Relations.CLASS);
            page.getLinks().add(classLink);
        }

        XWikiContext xcontext = xcontextProvider.get();

        // Add attachments
        if (withAttachments) {
            page.setAttachments(objectFactory.createAttachments());

            for (com.xpn.xwiki.api.Attachment attachment : doc.getAttachmentList()) {
                URL url = xcontext.getURLFactory().createAttachmentURL(attachment.getFilename(), doc.getSpace(),
                        doc.getName(), "download", null, doc.getWiki(), xcontext);
                String attachmentXWikiAbsoluteUrl = url.toString();
                String attachmentXWikiRelativeUrl = xcontext.getURLFactory().getURL(url, xcontext);

                page.getAttachments().getAttachments().add(toRestAttachment(baseUri, attachment,
                        attachmentXWikiRelativeUrl, attachmentXWikiAbsoluteUrl, withPrettyNames, false));
            }
        }

        // Add objects
        if (withObjects) {
            page.setObjects(objectFactory.createObjects());

            XWikiDocument xwikiDocument = xcontext.getWiki().getDocument(doc.getDocumentReference(), xcontext);

            for (List<BaseObject> objects : xwikiDocument.getXObjects().values()) {
                for (BaseObject object : objects) {
                    page.getObjects().getObjectSummaries()
                            .add(toRestObject(baseUri, doc, object, false, withPrettyNames));
                }
            }
        }

        // Add xclass
        if (withXClass) {
            page.setClazz(toRestClass(baseUri, doc.getxWikiClass()));
        }

        return page;
    }

    public Class toRestClass(URI baseUri, com.xpn.xwiki.api.Class xwikiClass) {
        Class clazz = this.objectFactory.createClass();
        clazz.setId(xwikiClass.getName());
        clazz.setName(xwikiClass.getName());

        DocumentReference reference = xwikiClass.getReference();
        String wikiName = reference.getWikiReference().getName();

        for (java.lang.Object xwikiPropertyClassObject : xwikiClass.getProperties()) {
            PropertyClass xwikiPropertyClass = (PropertyClass) xwikiPropertyClassObject;

            Property property = this.objectFactory.createProperty();
            property.setName(xwikiPropertyClass.getName());
            property.setType(xwikiPropertyClass.getxWikiClass().getName());

            for (java.lang.Object xwikiPropertyObject : xwikiPropertyClass.getProperties()) {
                com.xpn.xwiki.api.Property xwikiProperty = (com.xpn.xwiki.api.Property) xwikiPropertyObject;
                java.lang.Object value = xwikiProperty.getValue();

                Attribute attribute = this.objectFactory.createAttribute();
                attribute.setName(xwikiProperty.getName());

                if (value != null) {
                    attribute.setValue(value.toString());
                } else {
                    attribute.setValue("");
                }

                property.getAttributes().add(attribute);
            }

            String propertyUri = Utils.createURI(baseUri, ClassPropertyResource.class, wikiName,
                    xwikiClass.getName(), xwikiPropertyClass.getName()).toString();
            Link propertyLink = this.objectFactory.createLink();
            propertyLink.setHref(propertyUri);
            propertyLink.setRel(Relations.SELF);
            property.getLinks().add(propertyLink);

            clazz.getProperties().add(property);
        }

        String classUri = Utils.createURI(baseUri, ClassResource.class, wikiName, xwikiClass.getName()).toString();
        Link classLink = this.objectFactory.createLink();
        classLink.setHref(classUri);
        classLink.setRel(Relations.SELF);
        clazz.getLinks().add(classLink);

        String propertiesUri = Utils
                .createURI(baseUri, ClassPropertiesResource.class, wikiName, xwikiClass.getName()).toString();
        Link propertyLink = this.objectFactory.createLink();
        propertyLink.setHref(propertiesUri);
        propertyLink.setRel(Relations.PROPERTIES);
        clazz.getLinks().add(propertyLink);

        String objectsUri = Utils
                .createURI(baseUri, AllObjectsForClassNameResource.class, wikiName, xwikiClass.getName())
                .toString();
        Link objectsLink = this.objectFactory.createLink();
        objectsLink.setHref(objectsUri);
        objectsLink.setRel(Relations.OBJECTS);
        clazz.getLinks().add(objectsLink);

        return clazz;
    }

    public Attachment toRestAttachment(URI baseUri, com.xpn.xwiki.api.Attachment xwikiAttachment,
            String xwikiRelativeUrl, String xwikiAbsoluteUrl, Boolean withPrettyNames, boolean versionURL) {
        Attachment attachment = this.objectFactory.createAttachment();

        Document doc = xwikiAttachment.getDocument();

        attachment.setId(String.format("%s@%s", doc.getPrefixedFullName(), xwikiAttachment.getFilename()));
        attachment.setName(xwikiAttachment.getFilename());
        attachment.setSize(xwikiAttachment.getFilesize());
        attachment.setVersion(xwikiAttachment.getVersion());
        attachment.setPageId(doc.getPrefixedFullName());
        attachment.setPageVersion(doc.getVersion());
        attachment.setMimeType(xwikiAttachment.getMimeType());
        attachment.setAuthor(xwikiAttachment.getAuthor());
        if (withPrettyNames) {
            XWikiContext xcontext = xcontextProvider.get();
            attachment.setAuthorName(
                    xcontext.getWiki().getUserName(xwikiAttachment.getAuthor(), null, false, xcontext));
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(xwikiAttachment.getDate());
        attachment.setDate(calendar);

        attachment.setXwikiRelativeUrl(xwikiRelativeUrl);
        attachment.setXwikiAbsoluteUrl(xwikiAbsoluteUrl);

        String pageUri = Utils.createURI(baseUri, PageResource.class, doc.getWiki(),
                Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName()).toString();
        Link pageLink = objectFactory.createLink();
        pageLink.setHref(pageUri);
        pageLink.setRel(Relations.PAGE);
        attachment.getLinks().add(pageLink);

        String attachmentUri;
        if (versionURL) {
            attachmentUri = Utils.createURI(baseUri, AttachmentVersionResource.class, doc.getWiki(),
                    Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), xwikiAttachment.getFilename(),
                    xwikiAttachment.getVersion()).toString();
        } else {
            attachmentUri = Utils.createURI(baseUri, AttachmentResource.class, doc.getWiki(),
                    Utils.getSpacesFromSpaceId(doc.getSpace()), doc.getName(), xwikiAttachment.getFilename())
                    .toString();
        }

        Link attachmentLink = objectFactory.createLink();
        attachmentLink.setHref(attachmentUri);
        attachmentLink.setRel(Relations.ATTACHMENT_DATA);
        attachment.getLinks().add(attachmentLink);

        return attachment;
    }

    /**
     * Serializes the value of the given XObject property.
     * 
     * @param property an XObject property
     * @return the String representation of the property value
     */
    private static String serializePropertyValue(PropertyInterface property) {
        if (property == null) {
            return "";
        }

        java.lang.Object value = ((BaseProperty) property).getValue();
        if (value instanceof List) {
            return StringUtils.join((List) value, "|");
        } else if (value != null) {
            return value.toString();
        } else {
            return "";
        }
    }

    public JobRequest toRestJobRequest(Request request) throws XWikiRestException {
        JobRequest restJobRequest = this.objectFactory.createJobRequest();

        restJobRequest.setId(toRestJobId(request.getId()));
        restJobRequest.setInteractive(request.isInteractive());
        restJobRequest.setRemote(request.isRemote());
        restJobRequest.setVerbose(request.isVerbose());

        for (String key : request.getPropertyNames()) {
            restJobRequest.getProperties().add(toRestMapEntry(key, request.getProperty(key)));
        }

        return restJobRequest;
    }

    public JobId toRestJobId(List<String> id) {
        if (id == null) {
            return null;
        }

        JobId restJobId = this.objectFactory.createJobId();

        restJobId.withElements(id);

        return restJobId;
    }

    public MapEntry toRestMapEntry(String key, java.lang.Object value) throws XWikiRestException {
        MapEntry restMapEntry = this.objectFactory.createMapEntry();

        restMapEntry.setKey(key);
        try {
            restMapEntry.setValue(this.jaxbConverter.serializeAny(value));
        } catch (ParserConfigurationException e) {
            throw new XWikiRestException("Failed to serialize property [" + key + "] with value [" + value + "]",
                    e);
        }

        return restMapEntry;
    }

    public DefaultRequest toJobRequest(JobRequest restJobRequest) {
        DefaultRequest request = new DefaultRequest();

        if (restJobRequest.getId() != null) {
            request.setId(restJobRequest.getId().getElements());
        }

        request.setInteractive(restJobRequest.isInteractive());
        request.setVerbose(restJobRequest.isVerbose());

        for (MapEntry restEntry : restJobRequest.getProperties()) {
            request.setProperty(restEntry.getKey(), this.jaxbConverter.unserializeAny(restEntry.getValue()));
        }

        return request;
    }

    public JobStatus toRestJobStatus(org.xwiki.job.event.status.JobStatus jobStatus, URI self, boolean request,
            boolean progress, boolean log, String logFromLevel) throws XWikiRestException {
        JobStatus status = this.objectFactory.createJobStatus();
        status.setId(StringUtils.join(jobStatus.getRequest().getId(), "/"));
        status.setState(jobStatus.getState().name());
        if (jobStatus.getStartDate() != null) {
            Calendar calendarStartDate = Calendar.getInstance();
            calendarStartDate.setTime(jobStatus.getStartDate());
            status.setStartDate(calendarStartDate);
        }
        if (jobStatus.getEndDate() != null) {
            Calendar calendarEndDate = Calendar.getInstance();
            calendarEndDate.setTime(jobStatus.getEndDate());
            status.setEndDate(calendarEndDate);
        }

        // Request
        if (request) {
            status.setRequest(toRestJobRequest(jobStatus.getRequest()));
        }

        // Progress
        if (progress) {
            status.setProgress(toRestJobProgress(jobStatus.getProgress()));
        }

        // Log
        if (log) {
            status.setLog(toRestJobLog(jobStatus.getLog(), self, null, logFromLevel));
        }

        // Link
        if (self != null) {
            Link link = objectFactory.createLink();
            link.setHref(self.toString());
            link.setRel(Relations.SELF);
            status.getLinks().add(link);
        }

        return status;
    }

    public JobProgress toRestJobProgress(org.xwiki.job.event.status.JobProgress progress) {
        JobProgress restJobProgress = this.objectFactory.createJobProgress();

        restJobProgress.setOffset(progress.getOffset());
        restJobProgress.setCurrentLevelOffset(progress.getCurrentLevelOffset());

        // TODO: add support for steps

        return restJobProgress;
    }

    public JobLog toRestJobLog(LogQueue logQueue, URI self, String level, String fromLevel) {
        // Filter log
        Collection<LogEvent> logs;
        if (level != null) {
            logs = logQueue.getLogs(LogLevel.valueOf(level.toUpperCase()));
        } else if (fromLevel != null) {
            logs = logQueue.getLogsFrom(LogLevel.valueOf(fromLevel.toUpperCase()));
        } else {
            logs = logQueue;
        }

        return toRestJobLog(logs, self);
    }

    public JobLog toRestJobLog(Collection<LogEvent> logs, URI self) {
        JobLog log = this.objectFactory.createJobLog();

        // Serialize log
        for (LogEvent logEvent : logs) {
            // TODO: add support for log arguments
            // TODO: add support for log Marker
            org.xwiki.rest.model.jaxb.LogEvent event = this.objectFactory.createLogEvent();
            event.setLevel(logEvent.getLevel().name());
            Calendar calendarDate = Calendar.getInstance();
            calendarDate.setTimeInMillis(logEvent.getTimeStamp());
            event.setDate(calendarDate);
            event.setFormattedMessage(logEvent.getFormattedMessage());
            log.getLogEvents().add(event);
        }

        // Set link
        if (self != null) {
            Link link = this.objectFactory.createLink();
            link.setHref(self.toString());
            link.setRel(Relations.SELF);
            log.getLinks().add(link);
        }

        return log;
    }
}