OLINGO-878: Adding support to remove unvalid xml characters from Atom payload
This commit is contained in:
parent
3c205f907e
commit
d880d6c480
|
@ -28,6 +28,7 @@ public class ComplexSerializerOptions {
|
|||
private ContextURL contextURL;
|
||||
private ExpandOption expand;
|
||||
private SelectOption select;
|
||||
private String xml10InvalidCharReplacement;
|
||||
|
||||
/** Gets the {@link ContextURL}. */
|
||||
public ContextURL getContextURL() {
|
||||
|
@ -44,6 +45,11 @@ public class ComplexSerializerOptions {
|
|||
return select;
|
||||
}
|
||||
|
||||
/** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */
|
||||
public String xml10InvalidCharReplacement() {
|
||||
return xml10InvalidCharReplacement;
|
||||
}
|
||||
|
||||
private ComplexSerializerOptions() {}
|
||||
|
||||
/** Initializes the options builder. */
|
||||
|
@ -77,7 +83,13 @@ public class ComplexSerializerOptions {
|
|||
options.select = select;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** set the replacement string for xml 1.0 unicode controlled characters that are not allowed */
|
||||
public Builder xml10InvalidCharReplacement(final String replacement) {
|
||||
options.xml10InvalidCharReplacement = replacement;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the OData serializer options. */
|
||||
public ComplexSerializerOptions build() {
|
||||
return options;
|
||||
|
|
|
@ -32,6 +32,7 @@ public class EntityCollectionSerializerOptions {
|
|||
private SelectOption select;
|
||||
private boolean writeOnlyReferences;
|
||||
private String id;
|
||||
private String xml10InvalidCharReplacement;
|
||||
|
||||
/** Gets the {@link ContextURL}. */
|
||||
public ContextURL getContextURL() {
|
||||
|
@ -63,6 +64,11 @@ public class EntityCollectionSerializerOptions {
|
|||
return id;
|
||||
}
|
||||
|
||||
/** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */
|
||||
public String xml10InvalidCharReplacement() {
|
||||
return xml10InvalidCharReplacement;
|
||||
}
|
||||
|
||||
/** Initializes the options builder. */
|
||||
public static Builder with() {
|
||||
return new Builder();
|
||||
|
@ -112,7 +118,13 @@ public class EntityCollectionSerializerOptions {
|
|||
options.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** set the replacement String for xml 1.0 unicode controlled characters that are not allowed */
|
||||
public Builder xml10InvalidCharReplacement(final String replacement) {
|
||||
options.xml10InvalidCharReplacement = replacement;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the OData serializer options. */
|
||||
public EntityCollectionSerializerOptions build() {
|
||||
return options;
|
||||
|
|
|
@ -28,6 +28,7 @@ public class EntitySerializerOptions {
|
|||
private ExpandOption expand;
|
||||
private SelectOption select;
|
||||
private boolean writeOnlyReferences;
|
||||
private String xml10InvalidCharReplacement;
|
||||
|
||||
/** Gets the {@link ContextURL}. */
|
||||
public ContextURL getContextURL() {
|
||||
|
@ -49,6 +50,11 @@ public class EntitySerializerOptions {
|
|||
return writeOnlyReferences;
|
||||
}
|
||||
|
||||
/** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */
|
||||
public String xml10InvalidCharReplacement() {
|
||||
return xml10InvalidCharReplacement;
|
||||
}
|
||||
|
||||
private EntitySerializerOptions() {}
|
||||
|
||||
/** Initializes the options builder. */
|
||||
|
@ -88,7 +94,13 @@ public class EntitySerializerOptions {
|
|||
options.writeOnlyReferences = ref;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** set the replacement string for xml 1.0 unicode controlled characters that are not allowed */
|
||||
public Builder xml10InvalidCharReplacement(final String replacement) {
|
||||
options.xml10InvalidCharReplacement = replacement;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the OData serializer options. */
|
||||
public EntitySerializerOptions build() {
|
||||
return options;
|
||||
|
|
|
@ -30,6 +30,7 @@ public final class PrimitiveSerializerOptions {
|
|||
private Integer precision;
|
||||
private Integer scale;
|
||||
private Boolean isUnicode;
|
||||
private String xml10InvalidCharReplacement;
|
||||
|
||||
/** Gets the {@link ContextURL}. */
|
||||
public ContextURL getContextURL() {
|
||||
|
@ -60,6 +61,12 @@ public final class PrimitiveSerializerOptions {
|
|||
public Boolean isUnicode() {
|
||||
return isUnicode;
|
||||
}
|
||||
|
||||
/** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */
|
||||
public String xml10InvalidCharReplacement() {
|
||||
return xml10InvalidCharReplacement;
|
||||
}
|
||||
|
||||
|
||||
private PrimitiveSerializerOptions() {}
|
||||
|
||||
|
@ -123,6 +130,12 @@ public final class PrimitiveSerializerOptions {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** set the replacement string for xml 1.0 unicode controlled characters that are not allowed */
|
||||
public Builder xml10InvalidCharReplacement(final String replacement) {
|
||||
options.xml10InvalidCharReplacement = replacement;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the OData serializer options. */
|
||||
public PrimitiveSerializerOptions build() {
|
||||
return options;
|
||||
|
|
|
@ -28,6 +28,7 @@ public class PrimitiveValueSerializerOptions {
|
|||
private Integer precision;
|
||||
private Integer scale;
|
||||
private Boolean isUnicode;
|
||||
private String xml10InvalidCharReplacement;
|
||||
|
||||
/** Gets the nullable facet. */
|
||||
public Boolean isNullable() {
|
||||
|
@ -53,6 +54,11 @@ public class PrimitiveValueSerializerOptions {
|
|||
public Boolean isUnicode() {
|
||||
return isUnicode;
|
||||
}
|
||||
|
||||
/** Gets the replacement string for unicode characters, that is not allowed in XML 1.0 */
|
||||
public String xml10InvalidCharReplacement() {
|
||||
return xml10InvalidCharReplacement;
|
||||
}
|
||||
|
||||
private PrimitiveValueSerializerOptions() {}
|
||||
|
||||
|
@ -109,7 +115,13 @@ public class PrimitiveValueSerializerOptions {
|
|||
options.isUnicode = property.isUnicode();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/** set the replacement string for xml 1.0 unicode controlled characters that are not allowed */
|
||||
public Builder xml10InvalidCharReplacement(final String replacement) {
|
||||
options.xml10InvalidCharReplacement = replacement;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the OData serializer options. */
|
||||
public PrimitiveValueSerializerOptions build() {
|
||||
return options;
|
||||
|
|
|
@ -97,57 +97,93 @@ import org.apache.olingo.server.api.edmx.EdmxReferenceIncludeAnnotation;
|
|||
public class MetadataParser {
|
||||
private boolean parseAnnotations = false;
|
||||
private static final String XML_LINK_NS = "http://www.w3.org/1999/xlink";
|
||||
private ReferenceResolver defaultReferenceResolver = new DefaultReferenceResolver();
|
||||
private boolean loadCoreVocabularies = false;
|
||||
private ReferenceResolver referenceResolver = new DefaultReferenceResolver();
|
||||
private boolean useLocalCoreVocabularies = true;
|
||||
private boolean implicitlyLoadCoreVocabularies = false;
|
||||
|
||||
/**
|
||||
* Avoid reading the annotations in the $metadata
|
||||
* @param parse
|
||||
* @return
|
||||
*/
|
||||
public MetadataParser parseAnnotations(boolean parse) {
|
||||
this.parseAnnotations = parse;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Externalize the reference loading, such that they can be loaded from local caches
|
||||
* @param resolver
|
||||
* @return
|
||||
*/
|
||||
public MetadataParser referenceResolver(ReferenceResolver resolver) {
|
||||
this.defaultReferenceResolver = resolver;
|
||||
this.referenceResolver = resolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MetadataParser loadCoreVocabularies(boolean load) {
|
||||
this.loadCoreVocabularies = load;
|
||||
/**
|
||||
* Load the core libraries from local classpath
|
||||
* @param load true for yes; false otherwise
|
||||
* @return
|
||||
*/
|
||||
public MetadataParser useLocalCoreVocabularies(boolean load) {
|
||||
this.useLocalCoreVocabularies = load;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the core vocabularies, irrespective of if they are defined in the $metadata
|
||||
* @param load
|
||||
* @return
|
||||
*/
|
||||
public MetadataParser implicitlyLoadCoreVocabularies(boolean load) {
|
||||
this.implicitlyLoadCoreVocabularies = load;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceMetadata buildServiceMetadata(Reader csdl) throws XMLStreamException {
|
||||
SchemaBasedEdmProvider provider = buildEdmProvider(csdl,
|
||||
this.defaultReferenceResolver, this.loadCoreVocabularies);
|
||||
SchemaBasedEdmProvider provider = buildEdmProvider(csdl, this.referenceResolver,
|
||||
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies);
|
||||
return new ServiceMetadataImpl(provider, provider.getReferences(), null);
|
||||
}
|
||||
|
||||
public SchemaBasedEdmProvider buildEdmProvider(Reader csdl) throws XMLStreamException {
|
||||
return buildEdmProvider(csdl, this.defaultReferenceResolver, this.loadCoreVocabularies);
|
||||
}
|
||||
|
||||
protected SchemaBasedEdmProvider buildEdmProvider(Reader csdl,
|
||||
ReferenceResolver referenceResolver, boolean loadCoreVocabularies) throws XMLStreamException {
|
||||
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
|
||||
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
|
||||
return buildEdmProvider(reader, referenceResolver, loadCoreVocabularies);
|
||||
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
|
||||
return buildEdmProvider(reader, this.referenceResolver,
|
||||
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies);
|
||||
}
|
||||
|
||||
protected SchemaBasedEdmProvider buildEdmProvider(Reader csdl,
|
||||
ReferenceResolver resolver, boolean loadCore, boolean useLocal)
|
||||
throws XMLStreamException {
|
||||
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
|
||||
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
|
||||
return buildEdmProvider(reader, resolver, loadCore, useLocal);
|
||||
}
|
||||
|
||||
protected SchemaBasedEdmProvider buildEdmProvider(InputStream csdl,
|
||||
ReferenceResolver referenceResolver, boolean loadCoreVocabularies) throws XMLStreamException {
|
||||
ReferenceResolver resolver, boolean loadCore, boolean useLocal)
|
||||
throws XMLStreamException {
|
||||
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
|
||||
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
|
||||
return buildEdmProvider(reader, referenceResolver, loadCoreVocabularies);
|
||||
return buildEdmProvider(reader, resolver, loadCore, useLocal);
|
||||
}
|
||||
|
||||
protected SchemaBasedEdmProvider buildEdmProvider(XMLEventReader reader,
|
||||
ReferenceResolver referenceResolver, boolean loadCoreVocabularies) throws XMLStreamException {
|
||||
SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider(referenceResolver);
|
||||
ReferenceResolver resolver, boolean loadCore, boolean useLocal)
|
||||
throws XMLStreamException {
|
||||
SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider();
|
||||
|
||||
final StringBuilder xmlBase = new StringBuilder();
|
||||
|
||||
new ElementReader<SchemaBasedEdmProvider>() {
|
||||
@Override
|
||||
void build(XMLEventReader reader, StartElement element, SchemaBasedEdmProvider provider,
|
||||
String name) throws XMLStreamException {
|
||||
String xmlBase = attrNS(element, XML_LINK_NS, "base");
|
||||
provider.setXMLBase(xmlBase);
|
||||
if (attrNS(element, XML_LINK_NS, "base") != null) {
|
||||
xmlBase.append(attrNS(element, XML_LINK_NS, "base"));
|
||||
}
|
||||
String version = attr(element, "Version");
|
||||
if ("4.0".equals(version)) {
|
||||
readDataServicesAndReference(reader, element, provider);
|
||||
|
@ -167,23 +203,100 @@ public class MetadataParser {
|
|||
event.asEndElement().getName().getLocalPart()));
|
||||
}
|
||||
|
||||
// load the core vocabularies
|
||||
if (loadCoreVocabularies) {
|
||||
loadVocabularySchema(provider, "Org.OData.Core.V1", "Org.OData.Core.V1.xml");
|
||||
loadVocabularySchema(provider, "Org.OData.Capabilities.V1", "Org.OData.Capabilities.V1.xml");
|
||||
loadVocabularySchema(provider, "Org.OData.Measures.V1", "Org.OData.Measures.V1.xml");
|
||||
}
|
||||
//load core vocabularies even though they are not defined in the references
|
||||
if (loadCore) {
|
||||
loadCoreVocabulary(provider, "Org.OData.Core.V1");
|
||||
loadCoreVocabulary(provider, "Org.OData.Capabilities.V1");
|
||||
loadCoreVocabulary(provider, "Org.OData.Measures.V1");
|
||||
}
|
||||
|
||||
// load all the reference schemas
|
||||
if (resolver != null) {
|
||||
loadReferencesSchemas(provider, xmlBase.length() == 0 ? null
|
||||
: fixXmlBase(xmlBase.toString()), resolver, loadCore, useLocal);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
private void loadVocabularySchema(SchemaBasedEdmProvider provider, String namespace,
|
||||
private void loadReferencesSchemas(SchemaBasedEdmProvider provider,
|
||||
String xmlBase, ReferenceResolver resolver, boolean loadCore,
|
||||
boolean useLocal) {
|
||||
|
||||
for (EdmxReference reference:provider.getReferences()) {
|
||||
try {
|
||||
SchemaBasedEdmProvider refProvider = null;
|
||||
|
||||
for (EdmxReferenceInclude include : reference.getIncludes()) {
|
||||
|
||||
// check if the schema is already loaded before.
|
||||
if (provider.getSchema(include.getNamespace()) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isCoreVocabulary(include.getNamespace()) && useLocal) {
|
||||
loadCoreVocabulary(provider, include.getNamespace());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (refProvider == null) {
|
||||
InputStream is = this.referenceResolver.resolveReference(reference.getUri(), xmlBase);
|
||||
if (is == null) {
|
||||
throw new EdmException("Failed to load Reference "+reference.getUri()+" loading failed");
|
||||
} else {
|
||||
// do not implicitly load core vocabularies any more. But if the
|
||||
// references loading the core vocabularies try to use local if we can
|
||||
refProvider = buildEdmProvider(is, resolver, false, useLocal);
|
||||
}
|
||||
}
|
||||
|
||||
CsdlSchema refSchema = refProvider.getSchema(include.getNamespace(), false);
|
||||
provider.addReferenceSchema(include.getNamespace(), refProvider);
|
||||
if (include.getAlias() != null) {
|
||||
refSchema.setAlias(include.getAlias());
|
||||
provider.addReferenceSchema(include.getAlias(), refProvider);
|
||||
}
|
||||
}
|
||||
} catch (XMLStreamException e) {
|
||||
throw new EdmException("Failed to load Reference "+reference.getUri()+" parsing failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadCoreVocabulary(SchemaBasedEdmProvider provider,
|
||||
String namespace) throws XMLStreamException {
|
||||
if(namespace.equalsIgnoreCase("Org.OData.Core.V1")) {
|
||||
loadLocalVocabularySchema(provider, "Org.OData.Core.V1", "Org.OData.Core.V1.xml");
|
||||
} else if (namespace.equalsIgnoreCase("Org.OData.Capabilities.V1")) {
|
||||
loadLocalVocabularySchema(provider, "Org.OData.Capabilities.V1", "Org.OData.Capabilities.V1.xml");
|
||||
} else if (namespace.equalsIgnoreCase("Org.OData.Measures.V1")) {
|
||||
loadLocalVocabularySchema(provider, "Org.OData.Measures.V1", "Org.OData.Measures.V1.xml");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCoreVocabulary(String namespace) {
|
||||
if(namespace.equalsIgnoreCase("Org.OData.Core.V1") ||
|
||||
namespace.equalsIgnoreCase("Org.OData.Capabilities.V1") ||
|
||||
namespace.equalsIgnoreCase("Org.OData.Measures.V1")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String fixXmlBase(String base) {
|
||||
if (base.endsWith("/")) {
|
||||
return base;
|
||||
}
|
||||
return base+"/";
|
||||
}
|
||||
|
||||
private void loadLocalVocabularySchema(SchemaBasedEdmProvider provider, String namespace,
|
||||
String resource) throws XMLStreamException {
|
||||
CsdlSchema schema = provider.getSchema(namespace, false);
|
||||
CsdlSchema schema = provider.getVocabularySchema(namespace);
|
||||
if (schema == null) {
|
||||
InputStream is = this.getClass().getClassLoader().getResourceAsStream(resource);
|
||||
if (is != null) {
|
||||
SchemaBasedEdmProvider childProvider = buildEdmProvider(is, null, false);
|
||||
provider.addSchema(childProvider.getSchema(namespace, false));
|
||||
SchemaBasedEdmProvider childProvider = buildEdmProvider(is, null, false, false);
|
||||
provider.addVocabularySchema(namespace, childProvider);
|
||||
} else {
|
||||
throw new XMLStreamException("failed to load "+resource+" core vocabulary");
|
||||
}
|
||||
|
@ -191,7 +304,8 @@ public class MetadataParser {
|
|||
}
|
||||
|
||||
private void readDataServicesAndReference(XMLEventReader reader,
|
||||
StartElement element, SchemaBasedEdmProvider provider) throws XMLStreamException {
|
||||
StartElement element, SchemaBasedEdmProvider provider)
|
||||
throws XMLStreamException {
|
||||
final ArrayList<EdmxReference> references = new ArrayList<EdmxReference>();
|
||||
new ElementReader<SchemaBasedEdmProvider>() {
|
||||
@Override
|
||||
|
@ -384,7 +498,9 @@ public class MetadataParser {
|
|||
CsdlTypeDefinition td = new CsdlTypeDefinition();
|
||||
td.setName(attr(element, "Name"));
|
||||
td.setUnderlyingType(new FullQualifiedName(attr(element, "UnderlyingType")));
|
||||
td.setUnicode(Boolean.parseBoolean(attr(element, "Unicode")));
|
||||
if (attr(element, "Unicode") != null) {
|
||||
td.setUnicode(Boolean.parseBoolean(attr(element, "Unicode")));
|
||||
}
|
||||
|
||||
String maxLength = attr(element, "MaxLength");
|
||||
if (maxLength != null) {
|
||||
|
@ -837,7 +953,9 @@ public class MetadataParser {
|
|||
property.setCollection(isCollectionType(element));
|
||||
property.setNullable(Boolean.parseBoolean(attr(element, "Nullable") == null ? "true" : attr(
|
||||
element, "Nullable")));
|
||||
property.setUnicode(Boolean.parseBoolean(attr(element, "Unicode")));
|
||||
if (attr(element, "Unicode") != null) {
|
||||
property.setUnicode(Boolean.parseBoolean(attr(element, "Unicode")));
|
||||
}
|
||||
|
||||
String maxLength = attr(element, "MaxLength");
|
||||
if (maxLength != null) {
|
||||
|
@ -1077,18 +1195,23 @@ public class MetadataParser {
|
|||
private static class DefaultReferenceResolver implements ReferenceResolver {
|
||||
@Override
|
||||
public InputStream resolveReference(URI referenceUri, String xmlBase) {
|
||||
URL schemaURL = null;
|
||||
InputStream in = null;
|
||||
try {
|
||||
if (referenceUri.isAbsolute()) {
|
||||
schemaURL = referenceUri.toURL();
|
||||
URL schemaURL = referenceUri.toURL();
|
||||
in = schemaURL.openStream();
|
||||
} else {
|
||||
if (xmlBase != null) {
|
||||
schemaURL = new URL(xmlBase+referenceUri.toString());
|
||||
URL schemaURL = new URL(xmlBase+referenceUri.toString());
|
||||
in = schemaURL.openStream();
|
||||
} else {
|
||||
throw new EdmException("No xml:base set to read the references from the metadata");
|
||||
in = this.getClass().getClassLoader().getResourceAsStream(referenceUri.getPath());
|
||||
if (in == null) {
|
||||
throw new EdmException("No xml:base set to read the references from the metadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
return schemaURL.openStream();
|
||||
return in;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new EdmException(e);
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -18,15 +18,11 @@
|
|||
*/
|
||||
package org.apache.olingo.server.core;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
|
||||
import org.apache.olingo.commons.api.edm.EdmException;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.provider.CsdlAction;
|
||||
import org.apache.olingo.commons.api.edm.provider.CsdlActionImport;
|
||||
|
@ -52,15 +48,11 @@ import org.apache.olingo.server.api.edmx.EdmxReferenceInclude;
|
|||
public class SchemaBasedEdmProvider implements CsdlEdmProvider {
|
||||
private final List<CsdlSchema> edmSchemas = new ArrayList<CsdlSchema>();
|
||||
private final Map<String, EdmxReference> references = new ConcurrentHashMap<String, EdmxReference>();
|
||||
private final Map<String, SchemaBasedEdmProvider> referenceSchemas
|
||||
= new ConcurrentHashMap<String, SchemaBasedEdmProvider>();
|
||||
private String xmlBase;
|
||||
private ReferenceResolver referenceResolver;
|
||||
private final Map<String, SchemaBasedEdmProvider> referenceSchemas =
|
||||
new ConcurrentHashMap<String, SchemaBasedEdmProvider>();
|
||||
private final Map<String, SchemaBasedEdmProvider> coreVocabularySchemas =
|
||||
new ConcurrentHashMap<String, SchemaBasedEdmProvider>();
|
||||
|
||||
public SchemaBasedEdmProvider(ReferenceResolver referenceResolver) {
|
||||
this.referenceResolver = referenceResolver;
|
||||
}
|
||||
|
||||
void addSchema(CsdlSchema schema) {
|
||||
this.edmSchemas.add(schema);
|
||||
}
|
||||
|
@ -68,7 +60,23 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider {
|
|||
List<EdmxReference> getReferences(){
|
||||
return new ArrayList<EdmxReference>(references.values());
|
||||
}
|
||||
|
||||
|
||||
void addReferenceSchema(String ns, SchemaBasedEdmProvider provider) {
|
||||
this.referenceSchemas.put(ns, provider);
|
||||
}
|
||||
|
||||
void addVocabularySchema(String ns, SchemaBasedEdmProvider provider) {
|
||||
this.coreVocabularySchemas.put(ns, provider);
|
||||
}
|
||||
|
||||
CsdlSchema getVocabularySchema(String ns) {
|
||||
SchemaBasedEdmProvider provider = this.coreVocabularySchemas.get(ns);
|
||||
if (provider != null) {
|
||||
return provider.getSchema(ns, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
CsdlSchema getSchema(String ns) {
|
||||
return getSchema(ns, true);
|
||||
}
|
||||
|
@ -79,46 +87,20 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider {
|
|||
return s;
|
||||
}
|
||||
}
|
||||
CsdlSchema s = null;
|
||||
if (checkReferences) {
|
||||
return getReferenceSchema(ns);
|
||||
s = getReferenceSchema(ns);
|
||||
if (s == null) {
|
||||
s = getVocabularySchema(ns);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return s;
|
||||
}
|
||||
|
||||
private CsdlSchema getReferenceSchema(String ns) {
|
||||
CsdlSchema getReferenceSchema(String ns) {
|
||||
if (ns == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.referenceSchemas.get(ns) == null) {
|
||||
EdmxReference reference = this.references.get(ns);
|
||||
if (reference != null) {
|
||||
SchemaBasedEdmProvider provider = null;
|
||||
if (this.referenceResolver == null) {
|
||||
throw new EdmException("Failed to load Reference "+reference.getUri());
|
||||
} else {
|
||||
InputStream is = this.referenceResolver.resolveReference(reference.getUri(), this.xmlBase);
|
||||
if (is != null) {
|
||||
try {
|
||||
MetadataParser parser = new MetadataParser();
|
||||
provider = parser.buildEdmProvider(is, this.referenceResolver, false);
|
||||
} catch (XMLStreamException e) {
|
||||
throw new EdmException("Failed to load Reference "+reference.getUri()+" parsing failed");
|
||||
}
|
||||
} else {
|
||||
throw new EdmException("Failed to load Reference "+reference.getUri()+" loading failed");
|
||||
}
|
||||
}
|
||||
// copy references
|
||||
for (EdmxReferenceInclude include : reference.getIncludes()) {
|
||||
this.referenceSchemas.put(include.getNamespace(), provider);
|
||||
if (include.getAlias() != null) {
|
||||
CsdlSchema schema = provider.getSchema(include.getNamespace());
|
||||
schema.setAlias(include.getAlias());
|
||||
this.referenceSchemas.put(include.getAlias(), provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.referenceSchemas.get(ns) != null) {
|
||||
return this.referenceSchemas.get(ns).getSchema(ns);
|
||||
|
@ -133,7 +115,7 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CsdlEnumType getEnumType(FullQualifiedName fqn) throws ODataException {
|
||||
CsdlSchema schema = getSchema(fqn.getNamespace());
|
||||
|
@ -402,15 +384,5 @@ public class SchemaBasedEdmProvider implements CsdlEdmProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setXMLBase(String base) {
|
||||
if (base != null) {
|
||||
if (base.endsWith("/")) {
|
||||
this.xmlBase = base;
|
||||
} else {
|
||||
this.xmlBase = base+"/";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,14 +27,18 @@ import java.util.Map;
|
|||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.olingo.commons.api.data.ContextURL;
|
||||
import org.apache.olingo.commons.api.edm.EdmAnnotation;
|
||||
import org.apache.olingo.commons.api.edm.EdmSchema;
|
||||
import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
||||
import org.apache.olingo.commons.api.edm.annotation.EdmConstantExpression;
|
||||
import org.apache.olingo.commons.api.format.ContentType;
|
||||
import org.apache.olingo.commons.api.http.HttpHeader;
|
||||
import org.apache.olingo.commons.api.http.HttpMethod;
|
||||
import org.apache.olingo.server.api.OData;
|
||||
import org.apache.olingo.server.api.ODataApplicationException;
|
||||
import org.apache.olingo.server.api.ODataLibraryException;
|
||||
import org.apache.olingo.server.api.ODataRequest;
|
||||
import org.apache.olingo.server.api.ODataResponse;
|
||||
import org.apache.olingo.server.api.ODataLibraryException;
|
||||
import org.apache.olingo.server.api.ServiceMetadata;
|
||||
import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
|
||||
import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
|
||||
|
@ -140,26 +144,60 @@ public abstract class ServiceRequest {
|
|||
return this.request.getMethod() == HttpMethod.POST;
|
||||
}
|
||||
|
||||
private static FullQualifiedName XML10_CHAR_REPLACE_FQN = new FullQualifiedName(
|
||||
"org.apache.olingo.v1.xml10-incompatible-char-replacement");
|
||||
/**
|
||||
* Replacement character for the XML10 characters that are not supported.
|
||||
* @return
|
||||
*/
|
||||
protected String xml10IncompatibleCharReplacement() {
|
||||
for (EdmSchema schema : getServiceMetaData().getEdm().getSchemas()) {
|
||||
if (schema.getEntityContainer() != null) {
|
||||
for (EdmAnnotation annotation:schema.getAnnotations()) {
|
||||
if (annotation.getTerm() != null
|
||||
&& annotation.getTerm().getFullQualifiedName().equals(XML10_CHAR_REPLACE_FQN)) {
|
||||
EdmConstantExpression expr = annotation.getExpression().asConstant();
|
||||
return expr.getValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getSerializerOptions(Class<T> serilizerOptions, ContextURL contextUrl,
|
||||
boolean references) throws ContentNegotiatorException {
|
||||
public <T> T getSerializerOptions(Class<T> serilizerOptions,
|
||||
ContextURL contextUrl, boolean references) throws ContentNegotiatorException {
|
||||
|
||||
String xmlReplacement = null;
|
||||
if (getResponseContentType().isCompatible(ContentType.APPLICATION_XML)
|
||||
|| getResponseContentType().isCompatible(ContentType.APPLICATION_ATOM_XML)) {
|
||||
xmlReplacement = xml10IncompatibleCharReplacement();
|
||||
}
|
||||
|
||||
if (serilizerOptions.isAssignableFrom(EntitySerializerOptions.class)) {
|
||||
return (T) EntitySerializerOptions.with()
|
||||
.contextURL(isODataMetadataNone(getResponseContentType()) ? null : contextUrl)
|
||||
.expand(uriInfo.getExpandOption()).select(this.uriInfo.getSelectOption())
|
||||
.writeOnlyReferences(references).build();
|
||||
.writeOnlyReferences(references)
|
||||
.xml10InvalidCharReplacement(xmlReplacement)
|
||||
.build();
|
||||
} else if (serilizerOptions.isAssignableFrom(EntityCollectionSerializerOptions.class)) {
|
||||
return (T) EntityCollectionSerializerOptions.with()
|
||||
.contextURL(isODataMetadataNone(getResponseContentType()) ? null : contextUrl)
|
||||
.count(uriInfo.getCountOption()).expand(uriInfo.getExpandOption())
|
||||
.select(uriInfo.getSelectOption()).writeOnlyReferences(references)
|
||||
.id(getODataRequest().getRawBaseUri() + getODataRequest().getRawODataPath()).build();
|
||||
.id(getODataRequest().getRawBaseUri() + getODataRequest().getRawODataPath())
|
||||
.xml10InvalidCharReplacement(xmlReplacement)
|
||||
.build();
|
||||
} else if (serilizerOptions.isAssignableFrom(ComplexSerializerOptions.class)) {
|
||||
return (T) ComplexSerializerOptions.with().contextURL(contextUrl)
|
||||
.expand(this.uriInfo.getExpandOption()).select(this.uriInfo.getSelectOption()).build();
|
||||
.expand(this.uriInfo.getExpandOption()).select(this.uriInfo.getSelectOption())
|
||||
.xml10InvalidCharReplacement(xmlReplacement)
|
||||
.build();
|
||||
} else if (serilizerOptions.isAssignableFrom(PrimitiveSerializerOptions.class)) {
|
||||
return (T) PrimitiveSerializerOptions.with().contextURL(contextUrl)
|
||||
.xml10InvalidCharReplacement(xmlReplacement)
|
||||
.build();
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -248,10 +248,19 @@ public class DataRequest extends ServiceRequest {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getSerializerOptions(Class<T> serilizerOptions, ContextURL contextUrl, boolean references)
|
||||
throws ContentNegotiatorException {
|
||||
throws ContentNegotiatorException {
|
||||
if (serilizerOptions.isAssignableFrom(PrimitiveSerializerOptions.class)) {
|
||||
|
||||
String xmlReplacement = null;
|
||||
if (getResponseContentType().isCompatible(ContentType.APPLICATION_XML)
|
||||
|| getResponseContentType().isCompatible(ContentType.APPLICATION_ATOM_XML)) {
|
||||
xmlReplacement = xml10IncompatibleCharReplacement();
|
||||
}
|
||||
|
||||
return (T) PrimitiveSerializerOptions.with().contextURL(contextUrl)
|
||||
.facetsFrom(getUriResourceProperty().getProperty()).build();
|
||||
.facetsFrom(getUriResourceProperty().getProperty())
|
||||
.xml10InvalidCharReplacement(xmlReplacement)
|
||||
.build();
|
||||
}
|
||||
return super.getSerializerOptions(serilizerOptions, contextUrl, references);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
|
||||
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
|
||||
<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/os/vocabularies/Org.OData.Core.V1.xml">
|
||||
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1" />
|
||||
</edmx:Reference>
|
||||
<edmx:DataServices>
|
||||
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="org.apache.olingo.v1" Alias="olingo-extensions">
|
||||
|
||||
<Term Name="xml10-incompatible-char-replacement" Type="Edm.String" AppliesTo="PropertyValue ReturnType">
|
||||
<Annotation Term="Core.Description">
|
||||
<String>
|
||||
Replacement character for invalid characters in the XML 1.0 Atom payload
|
||||
</String>
|
||||
</Annotation>
|
||||
</Term>
|
||||
|
||||
</Schema>
|
||||
</edmx:DataServices>
|
||||
</edmx:Edmx>
|
|
@ -60,7 +60,7 @@ public class MetadataParserAnnotationsTest {
|
|||
public void setUp() throws Exception {
|
||||
MetadataParser parser = new MetadataParser();
|
||||
parser.parseAnnotations(true);
|
||||
parser.loadCoreVocabularies(true);
|
||||
parser.useLocalCoreVocabularies(true);
|
||||
provider = (CsdlEdmProvider) parser.buildEdmProvider(new FileReader("src/test/resources/annotations.xml"));
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,6 @@ public class MetadataParserTest {
|
|||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MetadataParser parser = new MetadataParser();
|
||||
parser.parseAnnotations(true);
|
||||
provider = (CsdlEdmProvider) parser.buildEdmProvider(new FileReader("src/test/resources/trippin.xml"));
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,9 @@ public class ServiceDispatcherTest {
|
|||
|
||||
public void beforeTest(ServiceHandler serviceHandler) throws Exception {
|
||||
MetadataParser parser = new MetadataParser();
|
||||
parser.parseAnnotations(true);
|
||||
parser.useLocalCoreVocabularies(true);
|
||||
parser.implicitlyLoadCoreVocabularies(true);
|
||||
ServiceMetadata metadata = parser.buildServiceMetadata(new FileReader("src/test/resources/trippin.xml"));
|
||||
|
||||
File baseDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
|
|
|
@ -114,20 +114,23 @@ public class TripPinServiceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEntitySet() throws Exception {
|
||||
HttpRequest req = new HttpGet(baseURL+"/People");
|
||||
req.setHeader("Content-Type", "application/json;odata.metadata=minimal");
|
||||
public void testXMLInvalidChars() throws Exception {
|
||||
HttpRequest req = new HttpGet(baseURL+"/Airlines('FM')");
|
||||
req.setHeader("Accept", "application/xml");
|
||||
|
||||
HttpResponse response = httpSend(req, 200);
|
||||
JsonNode node = getJSONNode(response);
|
||||
|
||||
assertEquals("$metadata#People", node.get("@odata.context").asText());
|
||||
assertEquals(baseURL+"/People?$skiptoken=8", node.get("@odata.nextLink").asText());
|
||||
|
||||
JsonNode person = ((ArrayNode)node.get("value")).get(0);
|
||||
assertEquals("russellwhyte", person.get("UserName").asText());
|
||||
String actual = IOUtils.toString(response.getEntity().getContent());
|
||||
String expected =
|
||||
"<m:properties>"
|
||||
+ "<d:AirlineCode>FM</d:AirlineCode>"
|
||||
+ "<d:Name>Shanghai xxxAirlinexxx</d:Name>"
|
||||
+ "<d:Picture m:null=\"true\"/>"
|
||||
+ "</m:properties>"
|
||||
+ "</a:content>"
|
||||
+"</a:entry>";
|
||||
assertTrue(actual.endsWith(expected));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReadEntitySetWithPaging() throws Exception {
|
||||
String url = baseURL+"/People";
|
||||
|
|
|
@ -51,7 +51,8 @@ public class TripPinServlet extends HttpServlet {
|
|||
|
||||
try {
|
||||
parser.parseAnnotations(true);
|
||||
parser.loadCoreVocabularies(true);
|
||||
parser.useLocalCoreVocabularies(true);
|
||||
parser.implicitlyLoadCoreVocabularies(true);
|
||||
metadata = parser.buildServiceMetadata(new FileReader("src/test/resources/trippin.xml"));
|
||||
} catch (XMLStreamException e) {
|
||||
throw new IOException(e);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
{
|
||||
"AirlineCode":"FM",
|
||||
"Name":"Shanghai Airline"
|
||||
"Name":"Shanghai \u0000Airline\u0001"
|
||||
},
|
||||
{
|
||||
"AirlineCode":"MU",
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
language governing permissions and limitations under the License. -->
|
||||
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"
|
||||
Version="4.0">
|
||||
<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/os/vocabularies/Org.OData.Core.V1.xml">
|
||||
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1" />
|
||||
</edmx:Reference>
|
||||
<edmx:DataServices>
|
||||
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm"
|
||||
Namespace="Org.OData.AnnoatationTest" Alias="test">
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License. -->
|
||||
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
|
||||
<edmx:Reference Uri="org.apache.olingo.v1.xml">
|
||||
<edmx:Include Alias="olingo-extensions" Namespace="org.apache.olingo.v1" />
|
||||
</edmx:Reference>
|
||||
<edmx:DataServices>
|
||||
<Schema Namespace="Microsoft.OData.SampleService.Models.TripPin"
|
||||
xmlns="http://docs.oasis-open.org/odata/ns/edm">
|
||||
|
@ -103,7 +106,7 @@
|
|||
<EnumMember>Org.OData.Core.V1.Permission/Read</EnumMember>
|
||||
</Annotation>
|
||||
</Property>
|
||||
<Property Name="Name" Type="Edm.String" Nullable="false" />
|
||||
<Property Name="Name" Type="Edm.String" Nullable="false"/>
|
||||
<Property Name="IataCode" Type="Edm.String" Nullable="false">
|
||||
<Annotation Term="Org.OData.Core.V1.Immutable" Bool="true" />
|
||||
</Property>
|
||||
|
@ -453,6 +456,7 @@
|
|||
</Annotation>
|
||||
<Annotation Term="Core.RequiresType" String="Edm.String" />
|
||||
</Term>
|
||||
<Annotation Term="org.apache.olingo.v1.xml10-incompatible-char-replacement" String="xxx"/>
|
||||
</Schema>
|
||||
</edmx:DataServices>
|
||||
</edmx:Edmx>
|
|
@ -52,6 +52,7 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
|
|||
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
|
||||
import org.apache.olingo.commons.api.ex.ODataErrorDetail;
|
||||
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
|
||||
import org.apache.olingo.commons.core.edm.primitivetype.EdmString;
|
||||
import org.apache.olingo.server.api.ODataServerError;
|
||||
import org.apache.olingo.server.api.ServiceMetadata;
|
||||
import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
|
||||
|
@ -250,10 +251,10 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
}
|
||||
|
||||
if (options == null) {
|
||||
writeEntitySet(metadata, entityType, entitySet, null, null, writer);
|
||||
writeEntitySet(metadata, entityType, entitySet, null, null, null, writer);
|
||||
} else {
|
||||
writeEntitySet(metadata, entityType, entitySet,
|
||||
options.getExpand(), options.getSelect(), writer);
|
||||
options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer);
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
|
@ -296,7 +297,9 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writer.writeStartDocument(DEFAULT_CHARSET, "1.0");
|
||||
writeEntity(metadata, entityType, entity, contextURL,
|
||||
options == null ? null : options.getExpand(),
|
||||
options == null ? null : options.getSelect(), writer, true);
|
||||
options == null ? null : options.getSelect(),
|
||||
options == null ? null : options.xml10InvalidCharReplacement(),
|
||||
writer, true);
|
||||
writer.writeEndDocument();
|
||||
|
||||
writer.flush();
|
||||
|
@ -336,15 +339,17 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
|
||||
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
|
||||
final EntityCollection entitySet, final ExpandOption expand, final SelectOption select,
|
||||
final XMLStreamWriter writer) throws XMLStreamException, SerializerException {
|
||||
final String xml10InvalidCharReplacement,final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
for (final Entity entity : entitySet.getEntities()) {
|
||||
writeEntity(metadata, entityType, entity, null, expand, select, writer, false);
|
||||
writeEntity(metadata, entityType, entity, null, expand, select, xml10InvalidCharReplacement, writer, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
|
||||
final Entity entity, final ContextURL contextURL, final ExpandOption expand,
|
||||
final SelectOption select, final XMLStreamWriter writer, final boolean top)
|
||||
final SelectOption select, final String xml10InvalidCharReplacement,
|
||||
final XMLStreamWriter writer, final boolean top)
|
||||
throws XMLStreamException, SerializerException {
|
||||
|
||||
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM);
|
||||
|
@ -397,7 +402,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
}
|
||||
|
||||
EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType());
|
||||
writeNavigationProperties(metadata, resolvedType, entity, expand, writer);
|
||||
writeNavigationProperties(metadata, resolvedType, entity, expand, xml10InvalidCharReplacement, writer);
|
||||
|
||||
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_CATEGORY, NS_ATOM);
|
||||
writer.writeAttribute(Constants.ATOM_ATTR_SCHEME, Constants.NS_SCHEME);
|
||||
|
@ -412,7 +417,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
}
|
||||
|
||||
writer.writeStartElement(METADATA, Constants.PROPERTIES, NS_METADATA);
|
||||
writeProperties(metadata, resolvedType, entity.getProperties(), select, writer);
|
||||
writeProperties(metadata, resolvedType, entity.getProperties(), select, xml10InvalidCharReplacement, writer);
|
||||
writer.writeEndElement(); // properties
|
||||
|
||||
if (!entityType.hasStream()) { // content
|
||||
|
@ -490,8 +495,8 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
}
|
||||
|
||||
protected void writeProperties(final ServiceMetadata metadata, final EdmStructuredType type,
|
||||
final List<Property> properties, final SelectOption select, final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
final List<Property> properties, final SelectOption select, final String xml10InvalidCharReplacement,
|
||||
final XMLStreamWriter writer) throws XMLStreamException, SerializerException {
|
||||
final boolean all = ExpandSelectHelper.isAll(select);
|
||||
final Set<String> selected = all ? new HashSet<String>() :
|
||||
ExpandSelectHelper.getSelectedPropertyNames(select.getSelectItems());
|
||||
|
@ -501,14 +506,15 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
final Property property = findProperty(propertyName, properties);
|
||||
final Set<List<String>> selectedPaths = all || edmProperty.isPrimitive() ? null :
|
||||
ExpandSelectHelper.getSelectedPaths(select.getSelectItems(), propertyName);
|
||||
writeProperty(metadata, edmProperty, property, selectedPaths, writer);
|
||||
writeProperty(metadata, edmProperty, property, selectedPaths, xml10InvalidCharReplacement, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeNavigationProperties(final ServiceMetadata metadata,
|
||||
final EdmStructuredType type, final Linked linked, final ExpandOption expand,
|
||||
final XMLStreamWriter writer) throws SerializerException, XMLStreamException {
|
||||
final String xml10InvalidCharReplacement, final XMLStreamWriter writer)
|
||||
throws SerializerException, XMLStreamException {
|
||||
if (ExpandSelectHelper.hasExpand(expand)) {
|
||||
final boolean expandAll = ExpandSelectHelper.isExpandAll(expand);
|
||||
final Set<String> expanded = expandAll ? new HashSet<String>() :
|
||||
|
@ -529,7 +535,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writeExpandedNavigationProperty(metadata, property, navigationLink,
|
||||
innerOptions == null ? null : innerOptions.getExpandOption(),
|
||||
innerOptions == null ? null : innerOptions.getSelectOption(),
|
||||
writer);
|
||||
xml10InvalidCharReplacement, writer);
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
@ -588,27 +594,28 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
|
||||
protected void writeExpandedNavigationProperty(final ServiceMetadata metadata,
|
||||
final EdmNavigationProperty property, final Link navigationLink,
|
||||
final ExpandOption innerExpand, final SelectOption innerSelect, final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
final ExpandOption innerExpand, final SelectOption innerSelect, final String xml10InvalidCharReplacement,
|
||||
final XMLStreamWriter writer) throws XMLStreamException, SerializerException {
|
||||
if (property.isCollection()) {
|
||||
if (navigationLink != null && navigationLink.getInlineEntitySet() != null) {
|
||||
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_FEED, NS_ATOM);
|
||||
writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
|
||||
innerSelect, writer);
|
||||
innerSelect, xml10InvalidCharReplacement, writer);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
} else {
|
||||
if (navigationLink != null && navigationLink.getInlineEntity() != null) {
|
||||
writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null,
|
||||
innerExpand, innerSelect, writer, false);
|
||||
innerExpand, innerSelect, xml10InvalidCharReplacement, writer, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeProperty(final ServiceMetadata metadata, final EdmProperty edmProperty,
|
||||
final Property property,
|
||||
final Set<List<String>> selectedPaths, final XMLStreamWriter writer) throws XMLStreamException,
|
||||
SerializerException {
|
||||
protected void writeProperty(final ServiceMetadata metadata,
|
||||
final EdmProperty edmProperty, final Property property,
|
||||
final Set<List<String>> selectedPaths,
|
||||
final String xml10InvalidCharReplacement, final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
writer.writeStartElement(DATA, edmProperty.getName(), NS_DATA);
|
||||
if (property == null || property.isNull()) {
|
||||
if (edmProperty.isNullable()) {
|
||||
|
@ -618,7 +625,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
SerializerException.MessageKeys.MISSING_PROPERTY, edmProperty.getName());
|
||||
}
|
||||
} else {
|
||||
writePropertyValue(metadata, edmProperty, property, selectedPaths, writer);
|
||||
writePropertyValue(metadata, edmProperty, property, selectedPaths, xml10InvalidCharReplacement, writer);
|
||||
}
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
@ -642,9 +649,11 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
return definedType;
|
||||
}
|
||||
|
||||
private void writePropertyValue(final ServiceMetadata metadata, final EdmProperty edmProperty,
|
||||
final Property property, final Set<List<String>> selectedPaths,
|
||||
final XMLStreamWriter writer) throws XMLStreamException, SerializerException {
|
||||
private void writePropertyValue(final ServiceMetadata metadata,
|
||||
final EdmProperty edmProperty, final Property property,
|
||||
final Set<List<String>> selectedPaths,
|
||||
final String xml10InvalidCharReplacement, final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
try {
|
||||
if (edmProperty.isPrimitive()
|
||||
|| edmProperty.getType().getKind() == EdmTypeKind.ENUM
|
||||
|
@ -657,22 +666,23 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writePrimitiveCollection((EdmPrimitiveType) edmProperty.getType(), property,
|
||||
edmProperty.isNullable(), edmProperty.getMaxLength(),
|
||||
edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(),
|
||||
writer);
|
||||
xml10InvalidCharReplacement,writer);
|
||||
} else {
|
||||
writePrimitive((EdmPrimitiveType) edmProperty.getType(), property,
|
||||
edmProperty.isNullable(), edmProperty.getMaxLength(),
|
||||
edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(),
|
||||
writer);
|
||||
xml10InvalidCharReplacement, writer);
|
||||
}
|
||||
} else if (property.isComplex()) {
|
||||
if (edmProperty.isCollection()) {
|
||||
writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_TYPE, collectionType(edmProperty.getType()));
|
||||
writeComplexCollection(metadata, (EdmComplexType) edmProperty.getType(), property, selectedPaths, writer);
|
||||
writeComplexCollection(metadata, (EdmComplexType) edmProperty.getType(), property, selectedPaths,
|
||||
xml10InvalidCharReplacement, writer);
|
||||
} else {
|
||||
writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_TYPE,
|
||||
"#" + complexType(metadata, (EdmComplexType) edmProperty.getType(), property.getType()));
|
||||
writeComplexValue(metadata, property, (EdmComplexType) edmProperty.getType(), property.asComplex().getValue(),
|
||||
selectedPaths, writer);
|
||||
selectedPaths, xml10InvalidCharReplacement, writer);
|
||||
}
|
||||
} else {
|
||||
throw new SerializerException("Property type not yet supported!",
|
||||
|
@ -687,14 +697,15 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
|
||||
private void writePrimitiveCollection(final EdmPrimitiveType type, final Property property,
|
||||
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
|
||||
final Boolean isUnicode,
|
||||
final Boolean isUnicode, final String xml10InvalidCharReplacement,
|
||||
final XMLStreamWriter writer) throws XMLStreamException, EdmPrimitiveTypeException, SerializerException {
|
||||
for (Object value : property.asCollection()) {
|
||||
writer.writeStartElement(METADATA, Constants.ELEM_ELEMENT, NS_METADATA);
|
||||
switch (property.getValueType()) {
|
||||
case COLLECTION_PRIMITIVE:
|
||||
case COLLECTION_ENUM:
|
||||
writePrimitiveValue(type, value, isNullable, maxLength, precision, scale, isUnicode, writer);
|
||||
writePrimitiveValue(type, value, isNullable, maxLength, precision,
|
||||
scale, isUnicode, xml10InvalidCharReplacement, writer);
|
||||
break;
|
||||
case COLLECTION_GEOSPATIAL:
|
||||
throw new SerializerException("Property type not yet supported!",
|
||||
|
@ -707,8 +718,9 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
}
|
||||
}
|
||||
|
||||
private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type,
|
||||
final Property property, final Set<List<String>> selectedPaths, final XMLStreamWriter writer)
|
||||
private void writeComplexCollection(final ServiceMetadata metadata,
|
||||
final EdmComplexType type, final Property property, final Set<List<String>> selectedPaths,
|
||||
final String xml10InvalidCharReplacement, final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
for (Object value : property.asCollection()) {
|
||||
writer.writeStartElement(METADATA, Constants.ELEM_ELEMENT, NS_METADATA);
|
||||
|
@ -717,7 +729,9 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
}
|
||||
switch (property.getValueType()) {
|
||||
case COLLECTION_COMPLEX:
|
||||
writeComplexValue(metadata, property, type, ((ComplexValue) value).getValue(), selectedPaths, writer);
|
||||
writeComplexValue(metadata, property, type,
|
||||
((ComplexValue) value).getValue(), selectedPaths,
|
||||
xml10InvalidCharReplacement, writer);
|
||||
break;
|
||||
default:
|
||||
throw new SerializerException("Property type not yet supported!",
|
||||
|
@ -729,7 +743,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
|
||||
private void writePrimitive(final EdmPrimitiveType type, final Property property,
|
||||
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
|
||||
final Boolean isUnicode, final XMLStreamWriter writer)
|
||||
final Boolean isUnicode, final String xml10InvalidCharReplacement, final XMLStreamWriter writer)
|
||||
throws EdmPrimitiveTypeException, XMLStreamException, SerializerException {
|
||||
if (property.isPrimitive()) {
|
||||
if (type != EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.String)) {
|
||||
|
@ -739,7 +753,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
type.getName());
|
||||
}
|
||||
writePrimitiveValue(type, property.asPrimitive(),
|
||||
isNullable, maxLength, precision, scale, isUnicode, writer);
|
||||
isNullable, maxLength, precision, scale, isUnicode, xml10InvalidCharReplacement, writer);
|
||||
} else if (property.isGeospatial()) {
|
||||
throw new SerializerException("Property type not yet supported!",
|
||||
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName());
|
||||
|
@ -747,7 +761,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_TYPE,
|
||||
"#" + type.getFullQualifiedName().getFullQualifiedNameAsString());
|
||||
writePrimitiveValue(type, property.asEnum(),
|
||||
isNullable, maxLength, precision, scale, isUnicode, writer);
|
||||
isNullable, maxLength, precision, scale, isUnicode, xml10InvalidCharReplacement, writer);
|
||||
} else {
|
||||
throw new SerializerException("Inconsistent property type!",
|
||||
SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, property.getName());
|
||||
|
@ -756,19 +770,23 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
|
||||
protected void writePrimitiveValue(final EdmPrimitiveType type, final Object primitiveValue,
|
||||
final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,
|
||||
final Boolean isUnicode,
|
||||
final Boolean isUnicode, final String xml10InvalidCharReplacement,
|
||||
final XMLStreamWriter writer) throws EdmPrimitiveTypeException, XMLStreamException {
|
||||
final String value = type.valueToString(primitiveValue,
|
||||
isNullable, maxLength, precision, scale, isUnicode);
|
||||
if (value == null) {
|
||||
writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_NULL, "true");
|
||||
} else {
|
||||
writer.writeCharacters(value);
|
||||
// XML 1.0 does not handle certain unicode characters, they need to be replaced
|
||||
writer.writeCharacters(replaceInvalidCharacters(type, value,
|
||||
isUnicode, xml10InvalidCharReplacement));
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeComplexValue(final ServiceMetadata metadata, Property complexProperty, final EdmComplexType type,
|
||||
final List<Property> properties, final Set<List<String>> selectedPaths, final XMLStreamWriter writer)
|
||||
protected void writeComplexValue(final ServiceMetadata metadata,
|
||||
Property complexProperty, final EdmComplexType type,
|
||||
final List<Property> properties, final Set<List<String>> selectedPaths,
|
||||
final String xml10InvalidCharReplacement, final XMLStreamWriter writer)
|
||||
throws XMLStreamException, SerializerException {
|
||||
|
||||
final EdmComplexType resolvedType = resolveComplexType(metadata,
|
||||
|
@ -779,7 +797,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
|
||||
writeProperty(metadata, (EdmProperty) resolvedType.getProperty(propertyName), property,
|
||||
selectedPaths == null ? null : ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
|
||||
writer);
|
||||
xml10InvalidCharReplacement, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -822,6 +840,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
options == null ? null : options.getPrecision(),
|
||||
options == null ? null : options.getScale(),
|
||||
options == null ? null : options.isUnicode(),
|
||||
options == null ? null : options.xml10InvalidCharReplacement(),
|
||||
writer);
|
||||
}
|
||||
writer.writeEndElement();
|
||||
|
@ -874,7 +893,10 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writer.writeAttribute(METADATA, NS_METADATA, Constants.ATTR_NULL, "true");
|
||||
} else {
|
||||
final List<Property> values = property.asComplex().getValue();
|
||||
writeProperties(metadata, resolvedType, values, options == null ? null : options.getSelect(), writer);
|
||||
writeProperties(metadata, resolvedType, values,
|
||||
options == null ? null : options.getSelect(),
|
||||
options == null ? null : options.xml10InvalidCharReplacement(),
|
||||
writer);
|
||||
}
|
||||
writer.writeEndDocument();
|
||||
writer.flush();
|
||||
|
@ -922,6 +944,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
options == null ? null : options.getPrecision(),
|
||||
options == null ? null : options.getScale(),
|
||||
options == null ? null : options.isUnicode(),
|
||||
options == null ? null : options.xml10InvalidCharReplacement(),
|
||||
writer);
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
|
@ -967,7 +990,8 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT,
|
||||
ContextURLBuilder.create(contextURL).toASCIIString());
|
||||
writeMetadataETag(metadata, writer);
|
||||
writeComplexCollection(metadata, type, property, null, writer);
|
||||
writeComplexCollection(metadata, type, property, null,
|
||||
options == null ? null:options.xml10InvalidCharReplacement(), writer);
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
writer.flush();
|
||||
|
@ -1104,4 +1128,30 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
|
|||
writer.writeAttribute(Constants.ATTR_HREF, entitySet.getNext().toASCIIString());
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
static String replaceInvalidCharacters(EdmPrimitiveType expectedType,
|
||||
String value, Boolean isUniCode, String invalidCharacterReplacement) {
|
||||
if (!(expectedType instanceof EdmString)
|
||||
|| invalidCharacterReplacement == null || isUniCode == null || !isUniCode) {
|
||||
return value;
|
||||
}
|
||||
String s = (String) value;
|
||||
StringBuilder result = null;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c <= 0x0020 && c != ' ' && c != '\n' && c != '\t' && c != '\r') {
|
||||
if (result == null) {
|
||||
result = new StringBuilder();
|
||||
result.append(s.substring(0, i));
|
||||
}
|
||||
result.append(invalidCharacterReplacement);
|
||||
} else if (result != null) {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
return value;
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1795,6 +1795,30 @@ public class ODataXmlSerializerTest {
|
|||
Assert.assertEquals(expected, resultString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXML10ReplacementChar() throws Exception {
|
||||
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
|
||||
final EdmProperty edmProperty = (EdmProperty) edmEntitySet.getEntityType().getProperty("PropertyString");
|
||||
final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName());
|
||||
property.setValue(ValueType.PRIMITIVE, "ab\u0000cd\u0001");
|
||||
final String resultString = IOUtils.toString(serializer
|
||||
.primitive(metadata, (EdmPrimitiveType) edmProperty.getType(), property,
|
||||
PrimitiveSerializerOptions.with()
|
||||
.contextURL(ContextURL.with()
|
||||
.entitySet(edmEntitySet).keyPath("32767").navOrPropertyPath(edmProperty.getName())
|
||||
.build())
|
||||
.xml10InvalidCharReplacement("XX")
|
||||
.unicode(Boolean.TRUE)
|
||||
.build()).getContent());
|
||||
|
||||
String expected = "<?xml version='1.0' encoding='UTF-8'?>"
|
||||
+ "<m:value xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" "
|
||||
+ "m:context=\"$metadata#ESAllPrim(32767)/PropertyString\" "
|
||||
+ "m:metadata-etag=\"metadataETag\">"
|
||||
+ "abXXcdXX</m:value>";
|
||||
Assert.assertEquals(expected, resultString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void primitivePropertyNull() throws Exception {
|
||||
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
|
||||
|
@ -1966,8 +1990,7 @@ public class ODataXmlSerializerTest {
|
|||
XMLAssert.assertXMLEqual(diff, true);
|
||||
}
|
||||
|
||||
private static class CustomDifferenceListener implements DifferenceListener {
|
||||
|
||||
public static class CustomDifferenceListener implements DifferenceListener {
|
||||
@Override
|
||||
public int differenceFound(Difference difference) {
|
||||
final String xpath = "/updated[1]/text()[1]";
|
||||
|
|
Loading…
Reference in New Issue