blob: f56e1c73ebbb694cbd30518f026be35754344022 [file] [log] [blame]
/*
* 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.chemistry.opencmis.client.util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.FolderTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.ItemTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PolicyTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyBooleanDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDateTimeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDecimalDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyHtmlDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyIdDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyIntegerDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyStringDefinition;
import org.apache.chemistry.opencmis.commons.definitions.PropertyUriDefinition;
import org.apache.chemistry.opencmis.commons.definitions.RelationshipTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.SecondaryTypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.Cardinality;
import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
import org.apache.chemistry.opencmis.commons.enums.DateTimeFormat;
import org.apache.chemistry.opencmis.commons.enums.PropertyType;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.impl.IOUtils;
import org.apache.chemistry.opencmis.commons.impl.JSONConverter;
import org.apache.chemistry.opencmis.commons.impl.XMLConstants;
import org.apache.chemistry.opencmis.commons.impl.XMLConverter;
import org.apache.chemistry.opencmis.commons.impl.XMLUtils;
import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParseException;
import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParser;
public final class TypeUtils {
private TypeUtils() {
}
/**
* Serializes the type definition to XML, using the format defined in the
* CMIS specification.
*
* The XML is UTF-8 encoded and the stream is not closed.
*/
public static void writeToXML(TypeDefinition type, OutputStream stream) throws XMLStreamException {
if (type == null) {
throw new IllegalArgumentException("Type must be set!");
}
if (stream == null) {
throw new IllegalArgumentException("Output stream must be set!");
}
XMLStreamWriter writer = XMLUtils.createWriter(stream);
XMLUtils.startXmlDocument(writer);
XMLConverter.writeTypeDefinition(writer, CmisVersion.CMIS_1_1, XMLConstants.NAMESPACE_CMIS, type);
XMLUtils.endXmlDocument(writer);
writer.close();
}
/**
* Serializes the type definition to JSON, using the format defined in the
* CMIS specification.
*
* The JSON is UTF-8 encoded and the stream is not closed.
*/
public static void writeToJSON(TypeDefinition type, OutputStream stream) throws IOException {
if (type == null) {
throw new IllegalArgumentException("Type must be set!");
}
if (stream == null) {
throw new IllegalArgumentException("Output stream must be set!");
}
Writer writer = new BufferedWriter(new OutputStreamWriter(stream, IOUtils.UTF8));
JSONConverter.convert(type, DateTimeFormat.SIMPLE).writeJSONString(writer);
writer.flush();
}
/**
* Reads a type definition from a XML stream.
*
* The stream must be UTF-8 encoded.
*/
public static TypeDefinition readFromXML(InputStream stream) throws XMLStreamException {
if (stream == null) {
throw new IllegalArgumentException("Input stream must be set!");
}
XMLStreamReader parser = XMLUtils.createParser(stream);
if (!XMLUtils.findNextStartElemenet(parser)) {
return null;
}
TypeDefinition typeDef = XMLConverter.convertTypeDefinition(parser);
parser.close();
return typeDef;
}
/**
* Reads a type definition from a JSON stream.
*
* The stream must be UTF-8 encoded.
*/
@SuppressWarnings("unchecked")
public static TypeDefinition readFromJSON(InputStream stream) throws IOException, JSONParseException {
if (stream == null) {
throw new IllegalArgumentException("Input stream must be set!");
}
JSONParser parser = new JSONParser();
Object json = parser.parse(new InputStreamReader(stream, IOUtils.UTF8));
if (!(json instanceof Map)) {
throw new CmisRuntimeException("Invalid stream! Not a type definition!");
}
return JSONConverter.convertTypeDefinition((Map<String, Object>) json);
}
private static boolean checkQueryName(String queryName) {
return queryName != null && queryName.length() > 0 && queryName.indexOf(' ') < 0 && queryName.indexOf('\t') < 0
&& queryName.indexOf('\n') < 0 && queryName.indexOf('\r') < 0 && queryName.indexOf('\f') < 0
&& queryName.indexOf(',') < 0 && queryName.indexOf('"') < 0 && queryName.indexOf('\'') < 0
&& queryName.indexOf('\\') < 0 && queryName.indexOf('.') < 0 && queryName.indexOf('(') < 0
&& queryName.indexOf(')') < 0;
}
/**
* Validates a type definition.
*
* @return the list of validation errors
*/
public static List<ValidationError> validateTypeDefinition(TypeDefinition type) {
if (type == null) {
throw new IllegalArgumentException("Type is null!");
}
List<ValidationError> errors = new ArrayList<TypeUtils.ValidationError>();
if (type.getId() == null || type.getId().length() == 0) {
errors.add(new ValidationError("id", "Type id must be set."));
}
if (type.getLocalName() == null || type.getLocalName().length() == 0) {
errors.add(new ValidationError("localName", "Local name must be set."));
}
if (type.getQueryName() != null) {
if (type.getQueryName().length() == 0) {
errors.add(new ValidationError("queryName", "Query name must not be empty."));
} else if (!checkQueryName(type.getQueryName())) {
errors.add(new ValidationError("queryName", "Query name contains invalid characters."));
}
}
if (type.isCreatable() == null) {
errors.add(new ValidationError("creatable", "Creatable flag must be set."));
}
if (type.isFileable() == null) {
errors.add(new ValidationError("fileable", "Fileable flag must be set."));
}
if (type.isQueryable() == null) {
errors.add(new ValidationError("queryable", "Queryable flag must be set."));
} else if (type.isQueryable().booleanValue()) {
if (type.getQueryName() == null || type.getQueryName().length() == 0) {
errors.add(new ValidationError("queryable",
"Queryable flag is set to TRUE, but the query name is not set."));
}
}
if (type.isControllablePolicy() == null) {
errors.add(new ValidationError("controllablePolicy", "ControllablePolicy flag must be set."));
}
if (type.isControllableAcl() == null) {
errors.add(new ValidationError("controllableACL", "ControllableACL flag must be set."));
}
if (type.isFulltextIndexed() == null) {
errors.add(new ValidationError("fulltextIndexed", "FulltextIndexed flag must be set."));
}
if (type.isIncludedInSupertypeQuery() == null) {
errors.add(new ValidationError("includedInSupertypeQuery", "IncludedInSupertypeQuery flag must be set."));
}
if (type.getBaseTypeId() == null) {
errors.add(new ValidationError("baseId", "Base type id must be set."));
} else if (!type.getBaseTypeId().value().equals(type.getParentTypeId())) {
if (type.getParentTypeId() == null || type.getParentTypeId().length() == 0) {
errors.add(new ValidationError("parentTypeId", "Parent type id must be set."));
}
}
if (type instanceof DocumentTypeDefinition) {
if (type.getBaseTypeId() != BaseTypeId.CMIS_DOCUMENT) {
errors.add(new ValidationError("baseId", "Base type id does not match the type."));
}
DocumentTypeDefinition docType = (DocumentTypeDefinition) type;
if (docType.isVersionable() == null) {
errors.add(new ValidationError("versionable", "Versionable flag must be set."));
}
if (docType.getContentStreamAllowed() == null) {
errors.add(new ValidationError("contentStreamAllowed", "ContentStreamAllowed flag must be set."));
}
} else if (type instanceof FolderTypeDefinition) {
if (type.getBaseTypeId() != BaseTypeId.CMIS_FOLDER) {
errors.add(new ValidationError("baseId", "Base type id does not match the type."));
}
} else if (type instanceof RelationshipTypeDefinition) {
if (type.getBaseTypeId() != BaseTypeId.CMIS_RELATIONSHIP) {
errors.add(new ValidationError("baseId", "Base type id does not match the type."));
}
} else if (type instanceof PolicyTypeDefinition) {
if (type.getBaseTypeId() != BaseTypeId.CMIS_POLICY) {
errors.add(new ValidationError("baseId", "Base type id does not match the type."));
}
} else if (type instanceof ItemTypeDefinition) {
if (type.getBaseTypeId() != BaseTypeId.CMIS_ITEM) {
errors.add(new ValidationError("baseId", "Base type id does not match the type."));
}
} else if (type instanceof SecondaryTypeDefinition) {
if (type.getBaseTypeId() != BaseTypeId.CMIS_SECONDARY) {
errors.add(new ValidationError("baseId", "Base type id does not match the type."));
}
} else {
errors.add(new ValidationError("baseId", "Unknown base interface."));
}
return errors;
}
/**
* Validates a property definition.
*
* @return the list of validation errors
*/
public static List<ValidationError> validatePropertyDefinition(PropertyDefinition<?> propDef) {
if (propDef == null) {
throw new IllegalArgumentException("Type is null!");
}
List<ValidationError> errors = new ArrayList<TypeUtils.ValidationError>();
if (propDef.getId() == null || propDef.getId().length() == 0) {
errors.add(new ValidationError("id", "Type id must be set."));
}
if (propDef.getQueryName() != null) {
if (propDef.getQueryName().length() == 0) {
errors.add(new ValidationError("queryName", "Query name must not be empty."));
} else if (!checkQueryName(propDef.getQueryName())) {
errors.add(new ValidationError("queryName", "Query name contains invalid characters."));
}
}
if (propDef.getCardinality() == null) {
errors.add(new ValidationError("cardinality", "Cardinality must be set."));
}
if (propDef.getUpdatability() == null) {
errors.add(new ValidationError("updatability", "Updatability must be set."));
}
if (propDef.isInherited() == null) {
errors.add(new ValidationError("inherited", "Inherited flag must be set."));
}
if (propDef.isRequired() == null) {
errors.add(new ValidationError("required", "Required flag must be set."));
}
if (propDef.isQueryable() == null) {
errors.add(new ValidationError("queryable", "Queryable flag must be set."));
} else if (propDef.isQueryable().booleanValue()) {
if (propDef.getQueryName() == null || propDef.getQueryName().length() == 0) {
errors.add(new ValidationError("queryable",
"Queryable flag is set to TRUE, but the query name is not set."));
}
}
if (propDef.isOrderable() == null) {
errors.add(new ValidationError("orderable", "Orderable flag must be set."));
} else if (propDef.isOrderable().booleanValue()) {
if (propDef.getCardinality() == Cardinality.MULTI) {
errors.add(new ValidationError("orderable", "Orderable flag is set to TRUE for a multi-value property."));
}
}
if (propDef.getPropertyType() == null) {
errors.add(new ValidationError("propertyType", "Property type id must be set."));
}
if (propDef instanceof PropertyIdDefinition) {
if (propDef.getPropertyType() != PropertyType.ID) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyStringDefinition) {
if (propDef.getPropertyType() != PropertyType.STRING) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyIntegerDefinition) {
if (propDef.getPropertyType() != PropertyType.INTEGER) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyDecimalDefinition) {
if (propDef.getPropertyType() != PropertyType.DECIMAL) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyBooleanDefinition) {
if (propDef.getPropertyType() != PropertyType.BOOLEAN) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyDateTimeDefinition) {
if (propDef.getPropertyType() != PropertyType.DATETIME) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyHtmlDefinition) {
if (propDef.getPropertyType() != PropertyType.HTML) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
} else if (propDef instanceof PropertyUriDefinition) {
if (propDef.getPropertyType() != PropertyType.URI) {
errors.add(new ValidationError("propertyType", "Property type does not match the property definition."));
}
}
return errors;
}
public static class ValidationError {
private final String attribute;
private final String error;
public ValidationError(String attribute, String error) {
this.attribute = attribute;
this.error = error;
}
public String getAttribute() {
return attribute;
}
public String getError() {
return error;
}
@Override
public String toString() {
return attribute + ": " + error;
}
}
}