blob: 78eeb22b2987ee8eca0c160bdb29913e7bdfc50a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.core.rest.resources;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.UUID;
import org.eclipse.skalli.commons.CollectionUtils;
import org.eclipse.skalli.commons.XMLUtils;
import org.eclipse.skalli.model.Derived;
import org.eclipse.skalli.model.ExtensibleEntityBase;
import org.eclipse.skalli.model.ExtensionEntityBase;
import org.eclipse.skalli.model.Member;
import org.eclipse.skalli.model.Project;
import org.eclipse.skalli.model.User;
import org.eclipse.skalli.services.entity.EntityServices;
import org.eclipse.skalli.services.extension.ExtensionService;
import org.eclipse.skalli.services.extension.ExtensionServices;
import org.eclipse.skalli.services.extension.rest.RestConverter;
import org.eclipse.skalli.services.extension.rest.RestConverterBase;
import org.eclipse.skalli.services.extension.rest.RestException;
import org.eclipse.skalli.services.extension.rest.RestUtils;
import org.eclipse.skalli.services.project.ProjectService;
import org.eclipse.skalli.services.rest.RestWriter;
import org.eclipse.skalli.services.user.UserService;
import org.eclipse.skalli.services.user.UserServices;
import org.restlet.data.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
public class CommonProjectConverter extends RestConverterBase<Project> {
private static final Logger LOG = LoggerFactory.getLogger(CommonProjectConverter.class);
public static final String API_VERSION = "1.4"; //$NON-NLS-1$
public static final String NAMESPACE = "http://www.eclipse.org/skalli/2010/API"; //$NON-NLS-1$
public static final String[] ALL_EXTENSIONS = new String[] { "*" }; //$NON-NLS-1$
private Set<String> extensions = Collections.emptySet();;
/**
* Create a <code>CommonProjectConverter</code> for rendering of
* projects without any extensions.
*/
public CommonProjectConverter() {
super(Project.class);
}
/**
* Create a <code>ProjectConverter</code> for rendering of
* projects with selected extensions.
*
* @param extensions the extensions to render, or <code>null</code>
* if no extensions should be rendered. If the array contains the
* entry <tt>"*"</tt> or {@link CommonProjectConverter#ALL_EXTENSIONS}
* is passed as argument, all extensions will be rendered.
*/
public CommonProjectConverter(String[] extensions) {
super(Project.class);
if (extensions != null) {
this.extensions = CollectionUtils.asSet(extensions);
}
}
// for testing purposes
void setRestWriter(RestWriter writer) {
this.writer = writer;
}
@SuppressWarnings("nls")
@Override
protected void marshal(Project project) throws IOException {
ProjectService projectService = ((ProjectService)EntityServices.getByEntityClass(Project.class));
UUID uuid = project.getUuid();
writer.pair("uuid", uuid);
writer.pair("id", project.getProjectId());
writer.pair("nature", projectService.getProjectNature(uuid).toString());
writer.pair("template", project.getProjectTemplateId());
writer.pair("name", project.getName());
writer.pair("shortName", project.getShortName());
writer.pair("phase", project.getPhase());
if (project.getRegistered() > 0) {
writer.timestamp("registered", project.getRegistered());
}
writer.pair("descriptionFormat", project.getDescriptionFormat());
writer.pair("description", project.getDescription());
writer.links();
writer.link(PROJECT_RELATION, RestUtils.URL_PROJECTS, uuid);
writer.link(PROJECT_PERMALINK, RestUtils.URL_BROWSE, uuid);
writer.link(BROWSE_RELATION, RestUtils.URL_BROWSE, project.getProjectId());
writer.link(ISSUES_RELATION, RestUtils.URL_PROJECTS, uuid, RestUtils.URL_ISSUES);
writer.link(SUBPROJECTS_RELATION, RestUtils.URL_PROJECTS, uuid, RestUtils.URL_SUBPROJECTS);
Project parent = project.getParentProject();
if (parent != null) {
marshalProjectLink(parent, PARENT_RELATION);
}
writer.end();
marshalSubprojects(project);
marshalMembers(uuid, projectService.getMembers(uuid), projectService.getMembersByRole(uuid));
marshalExtensions(project, ExtensionServices.getAll());
}
@SuppressWarnings("nls")
void marshalSubprojects(Project project) throws IOException {
writer.key("subprojects").array();
SortedSet<Project> subprojectList = project.getSubProjects();
if (subprojectList.size() > 0) {
for (Project subproject : subprojectList) {
marshalProjectLink(subproject, SUBPROJECT_RELATION);
}
}
writer.end();
}
@SuppressWarnings("nls")
void marshalProjectLink(Project project, String relation) throws IOException {
if (writer.isMediaType(MediaType.TEXT_XML)) {
writer.link(relation, RestUtils.URL_PROJECTS, project.getUuid());
} else {
writer.object();
writer.attribute("rel", relation);
writer.attribute("href", writer.hrefOf(RestUtils.URL_PROJECTS, project.getUuid()));
writer.attribute("uuid", project.getUuid());
writer.attribute("id", project.getProjectId());
writer.attribute("name", project.getName());
writer.end();
}
}
@SuppressWarnings("nls")
void marshalMembers(UUID uuid, SortedSet<Member> members, Map<String, SortedSet<Member>> membersByRole)
throws IOException {
writer.array("members", "member");
if (extensions.contains("*") || extensions.contains("members")) {
UserService userService = UserServices.getUserService();
for (Member member : members) {
String userId = member.getUserID();
writer.object();
writer.pair("userId", userId);
writer.link(USER_RELATION, RestUtils.URL_USERS, userId);
if (userService != null) {
User user = userService.getUserById(userId);
if (!user.isUnknown()) {
writer.pair("name", user.getDisplayName());
writer.pair("firstName", user.getFirstname());
writer.pair("lastName", user.getLastname());
writer.pair("email", user.getEmail());
}
}
if (writer.isMediaType(MediaType.TEXT_XML)) {
writer.array("role");
} else {
writer.key("roles").array();
}
for (Entry<String, SortedSet<Member>> entry : membersByRole.entrySet()) {
if (entry.getValue().contains(member)) {
writer.value(entry.getKey());
}
}
writer.end();
writer.end();
}
}
writer.end();
}
@SuppressWarnings("nls")
void marshalExtensions(Project project, Collection<ExtensionService<?>> extensionServices)
throws IOException {
writer.object("extensions");
for (ExtensionService<?> extensionService : extensionServices) {
if (extensions.contains("*") || extensions.contains(extensionService.getShortName())) {
marshalExtension(project, extensionService);
}
}
writer.end();
}
@SuppressWarnings("nls")
void marshalExtension(Project project, ExtensionService<?> extensionService) throws IOException {
Class<? extends ExtensionEntityBase> extensionClass = extensionService.getExtensionClass();
if (extensionClass.equals(Project.class)) {
return;
}
ExtensionEntityBase extension = project.getExtension(extensionClass);
RestConverter<?> converter = extensionService.getRestConverter();
if (extension != null && converter != null && converter.getConversionClass().isAssignableFrom(extensionClass)) {
writer.object(extensionService.getShortName());
namespaces(converter);
commonAttributes(extension, converter);
writer.attribute("inherited", project.isInherited(extensionClass));
writer.attribute("derived", extensionClass.isAnnotationPresent(Derived.class));
try {
converter.marshal(extension, writer);
} catch (Exception e) {
// don't let a buggy extension converter disturb the rendering of other converters!
LOG.error(MessageFormat.format("Failed to render extension ''{0}'' of project ''{1}''",
extensionService.getShortName(), project.getProjectId()), e);
}
writer.end();
}
}
@Override
protected Project unmarshal() throws RestException, IOException {
return unmarshal(new Project());
}
@SuppressWarnings("nls")
private Project unmarshal(Project project) throws RestException, IOException {
reader.object();
while (reader.hasMore()) {
if (reader.isKeyAnyOf("apiVersion")) {
String apiVersion = reader.attributeString();
if (!getApiVersion().equals(apiVersion)) {
throw new RestException(MessageFormat.format(
"Unsupported API version (requested: ''{0}'', expected: ''{1}'')",
apiVersion, getApiVersion()));
}
} else if (reader.isKeyAnyOf(XMLUtils.XMLNS)) {
String namespace = reader.attributeString();
if (!getNamespace().equals(namespace)) {
throw new RestException(MessageFormat.format(
"Unsupported namespace (requested: ''{0}'', expected: ''{1}'')",
namespace, getNamespace()));
}
} else if (reader.isKey("id")) {
project.setProjectId(reader.valueString());
} else if (reader.isKey("name")) {
project.setName(reader.valueString());
} else if (reader.isKey("shortName")) {
project.setShortName(reader.valueString());
} else if (reader.isKey("descriptionFormat")) {
project.setDescriptionFormat(reader.valueString());
} else if (reader.isKey("description")) {
project.setDescription(reader.valueString());
} else if (reader.isKey("template")) {
project.setProjectTemplateId(reader.valueString());
} else if (reader.isKey("phase")) {
project.setPhase(reader.valueString());
} else if (reader.isKey("extensions")) {
readExtensions(project);
} else {
reader.skip();
}
}
reader.end();
return project;
}
private void readExtensions(Project project) throws RestException, IOException {
reader.object();
while (reader.hasMore()) {
String shortName = reader.key();
ExtensionService<? extends ExtensionEntityBase> extensionService = ExtensionServices.getByShortName(shortName);
if (extensionService != null) {
InheritableExtensionConverter inhertableExtensionConverter =
new InheritableExtensionConverter(extensionService);
InheritableExtension inheritable = inhertableExtensionConverter.unmarshal(reader);
if (inheritable.isInherited() == Boolean.TRUE) {
project.setInherited(extensionService.getExtensionClass(), true);
} else {
project.addExtension(inheritable.getExtension());
}
}
}
reader.end();
}
@Deprecated
public CommonProjectConverter(String host) {
super(Project.class, "project", host); //$NON-NLS-1$
}
@Deprecated
public CommonProjectConverter(String host, String[] extensions) {
super(Project.class, "project", host); //$NON-NLS-1$
if (extensions != null) {
this.extensions = CollectionUtils.asSet(extensions);
}
}
@Deprecated
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
Project project = (Project) source;
UUID uuid = project.getUuid();
String host = getHost();
ProjectService projectService = ((ProjectService)EntityServices.getByEntityClass(Project.class));
writeNode(writer, "uuid", uuid.toString()); //$NON-NLS-1$
writeNode(writer, "id", project.getProjectId()); //$NON-NLS-1$
writeNode(writer, "nature", projectService.getProjectNature(uuid)); //$NON-NLS-1$
writeNode(writer, "template", project.getProjectTemplateId()); //$NON-NLS-1$
writeNode(writer, "name", project.getName()); //$NON-NLS-1$
writeNode(writer, "shortName", project.getShortName()); //$NON-NLS-1$
writeProjectLink(writer, PROJECT_RELATION, uuid);
writeLink(writer, BROWSE_RELATION, host + RestUtils.URL_BROWSE + project.getProjectId());
writeLink(writer, ISSUES_RELATION, host + RestUtils.URL_PROJECTS + uuid.toString() + RestUtils.URL_ISSUES);
writeNode(writer, "phase", project.getPhase()); //$NON-NLS-1$
if (project.getRegistered() > 0) {
writeDateTime(writer, "registered", project.getRegistered()); //$NON-NLS-1$
}
writeNode(writer, "description", project.getDescription()); //$NON-NLS-1$
UUID parent = project.getParentEntityId();
if (parent != null) {
writeProjectLink(writer, PARENT_RELATION, parent);
}
SortedSet<Project> subprojectList = project.getSubProjects();
if (subprojectList.size() > 0) {
writer.startNode("subprojects"); //$NON-NLS-1$
for (Project subproject : subprojectList) {
writeProjectLink(writer, SUBPROJECT_RELATION, subproject.getUuid());
}
writer.endNode();
}
marshalMembers(uuid, projectService, writer);
marshalExtensions(project, writer, context);
}
@Deprecated
private void marshalMembers(UUID uuid, ProjectService projectService, HierarchicalStreamWriter writer) {
if (extensions.contains("*") || extensions.contains("members")) { //$NON-NLS-1$ //$NON-NLS-2$
writer.startNode("members"); //$NON-NLS-1$
UserService userService = UserServices.getUserService();
for (Member member : projectService.getMembers(uuid)) {
writer.startNode("member"); //$NON-NLS-1$
String userId = member.getUserID();
writeNode(writer, "userId", userId); //$NON-NLS-1$
writeUserLink(writer, USER_RELATION, userId);
if (userService != null) {
User user = userService.getUserById(userId);
if (!user.isUnknown()) {
writeNode(writer, "name", user.getDisplayName()); //$NON-NLS-1$
writeNode(writer, "firstName", user.getFirstname()); //$NON-NLS-1$
writeNode(writer, "lastName", user.getLastname()); //$NON-NLS-1$
writeNode(writer, "email", user.getEmail()); //$NON-NLS-1$
}
}
for (Entry<String, SortedSet<Member>> entry : projectService.getMembersByRole(uuid).entrySet()) {
if (entry.getValue().contains(member)) {
writeNode(writer, "role", entry.getKey()); //$NON-NLS-1$
}
}
writer.endNode();
}
writer.endNode();
}
}
@Deprecated
private void marshalExtensions(ExtensibleEntityBase extensibleEntity, HierarchicalStreamWriter writer,
MarshallingContext context) {
writer.startNode("extensions"); //$NON-NLS-1$
for (ExtensionService<?> extensionService : ExtensionServices.getAll()) {
if (extensions.contains("*") || extensions.contains(extensionService.getShortName())) { //$NON-NLS-1$
marshalExtension(extensibleEntity, extensionService, writer, context);
}
}
writer.endNode();
}
@Deprecated
protected void marshalExtension(ExtensibleEntityBase extensibleEntity, ExtensionService<?> extensionService,
HierarchicalStreamWriter writer, MarshallingContext context) {
Class<? extends ExtensionEntityBase> extensionClass = extensionService.getExtensionClass();
if (extensionClass.equals(Project.class)) {
return;
}
ExtensionEntityBase extension = extensibleEntity.getExtension(extensionClass);
RestConverter<?> converter = extensionService.getRestConverter(getHost());
if (extension != null && converter != null) {
writer.startNode(extensionService.getShortName());
marshalNSAttributes(writer, converter);
marshalCommonAttributes(writer, extension, converter);
writer.addAttribute("inherited", Boolean.toString(extensibleEntity.isInherited(extensionClass))); //$NON-NLS-1$
writer.addAttribute("derived", Boolean.toString(extensionClass.isAnnotationPresent(Derived.class))); //$NON-NLS-1$
context.convertAnother(extension, converter);
writer.endNode();
}
}
@Deprecated
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
return null;
}
@Override
public String getApiVersion() {
return API_VERSION;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getXsdFileName() {
return "project.xsd"; //$NON-NLS-1$
}
}