OLINGO-861: Fixing the recursive load of the references, and nested annotations

This commit is contained in:
Ramesh Reddy 2017-04-10 14:50:51 -05:00
parent 89a6a69de7
commit 548c6e4a15
7 changed files with 187 additions and 35 deletions

View File

@ -100,6 +100,7 @@ public class MetadataParser {
private ReferenceResolver referenceResolver = new DefaultReferenceResolver();
private boolean useLocalCoreVocabularies = true;
private boolean implicitlyLoadCoreVocabularies = false;
private boolean recusivelyLoadReferences = false;
/**
* Avoid reading the annotations in the $metadata
@ -131,6 +132,16 @@ public class MetadataParser {
return this;
}
/**
* Load the core libraries from local classpath
* @param load true for yes; false otherwise
* @return
*/
public MetadataParser recursivelyLoadReferences(boolean load) {
this.recusivelyLoadReferences = load;
return this;
}
/**
* Load the core vocabularies, irrespective of if they are defined in the $metadata
* @param load
@ -143,7 +154,7 @@ public class MetadataParser {
public ServiceMetadata buildServiceMetadata(Reader csdl) throws XMLStreamException {
SchemaBasedEdmProvider provider = buildEdmProvider(csdl, this.referenceResolver,
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies);
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies, true);
return new ServiceMetadataImpl(provider, provider.getReferences(), null);
}
@ -151,27 +162,27 @@ public class MetadataParser {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
return buildEdmProvider(reader, this.referenceResolver,
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies);
this.implicitlyLoadCoreVocabularies, this.useLocalCoreVocabularies, true);
}
protected SchemaBasedEdmProvider buildEdmProvider(Reader csdl,
ReferenceResolver resolver, boolean loadCore, boolean useLocal)
ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas)
throws XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
return buildEdmProvider(reader, resolver, loadCore, useLocal);
return buildEdmProvider(reader, resolver, loadCore, useLocal, loadReferenceSchemas);
}
protected SchemaBasedEdmProvider buildEdmProvider(InputStream csdl,
ReferenceResolver resolver, boolean loadCore, boolean useLocal)
ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas)
throws XMLStreamException {
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
return buildEdmProvider(reader, resolver, loadCore, useLocal);
return buildEdmProvider(reader, resolver, loadCore, useLocal, loadReferenceSchemas);
}
protected SchemaBasedEdmProvider buildEdmProvider(XMLEventReader reader,
ReferenceResolver resolver, boolean loadCore, boolean useLocal)
ReferenceResolver resolver, boolean loadCore, boolean useLocal, boolean loadReferenceSchemas)
throws XMLStreamException {
SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider();
@ -211,7 +222,7 @@ public class MetadataParser {
}
// load all the reference schemas
if (resolver != null) {
if (resolver != null && loadReferenceSchemas) {
loadReferencesSchemas(provider, xmlBase.length() == 0 ? null
: fixXmlBase(xmlBase.toString()), resolver, loadCore, useLocal);
}
@ -245,15 +256,17 @@ public class MetadataParser {
} 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);
refProvider = buildEdmProvider(is, resolver, false, useLocal, this.recusivelyLoadReferences);
}
}
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);
if (refProvider != null) {
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) {
@ -295,7 +308,7 @@ public class MetadataParser {
if (schema == null) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(resource);
if (is != null) {
SchemaBasedEdmProvider childProvider = buildEdmProvider(is, null, false, false);
SchemaBasedEdmProvider childProvider = buildEdmProvider(is, null, false, false, true);
provider.addVocabularySchema(namespace, childProvider);
} else {
throw new XMLStreamException("failed to load "+resource+" core vocabulary");
@ -654,17 +667,19 @@ public class MetadataParser {
void build(XMLEventReader reader, StartElement element, T target, String name)
throws XMLStreamException {
// attribute based expressions.
readAttributeExpressions(element, target);
// element based expressions
for (ConstantExpressionType type:ConstantExpressionType.values()) {
if (name.equals(type.name())) {
if (reader.peek().isCharacters()) {
CsdlExpression expr = new CsdlConstantExpression(type, elementValue(reader, element));
write(target, expr);
}
}
if (!name.equals("Annotation")) {
// attribute based expressions.
readAttributeExpressions(element, target);
for (ConstantExpressionType type:ConstantExpressionType.values()) {
if (name.equals(type.name())) {
if (reader.peek().isCharacters()) {
CsdlExpression expr = new CsdlConstantExpression(type, elementValue(reader, element));
write(target, expr);
}
}
}
}
if (name.equals("Collection")) {
@ -719,6 +734,8 @@ public class MetadataParser {
expr.setType(attr(element, "Type"));
readPropertyValues(reader, element, expr);
write(target, expr);
} else if (name.equals("Annotation")) {
readAnnotations(reader, element, (CsdlAnnotatable)target);
}
}
}.read(reader, element, target, "Collection", "AnnotationPath",
@ -726,7 +743,7 @@ public class MetadataParser {
"Apply", "Function", "Cast", "If", "IsOf", "LabeledElement",
"LabeledElementReference", "Null", "Record","Binary", "Bool", "Date",
"DateTimeOffset", "Decimal", "Duration", "EnumMember", "Float", "Guid",
"Int", "String", "TimeOfDay");
"Int", "String", "TimeOfDay", "Annotation");
}
private <T> void readAttributeExpressions(StartElement element, T target)
@ -781,13 +798,17 @@ public class MetadataParser {
@Override
void build(XMLEventReader reader, StartElement element, CsdlRecord record, String name)
throws XMLStreamException {
CsdlPropertyValue value = new CsdlPropertyValue();
value.setProperty(attr(element, "Property"));
readAttributeExpressions(element, value);
readExpressions(reader, element, value);
record.getPropertyValues().add(value);
if (name.equals("PropertyValue")) {
CsdlPropertyValue value = new CsdlPropertyValue();
value.setProperty(attr(element, "Property"));
readAttributeExpressions(element, value);
readExpressions(reader, element, value);
record.getPropertyValues().add(value);
} else if (name.equals("Annotation")) {
readAnnotations(reader, element, record);
}
}
}.read(reader, element, record, "PropertyValue");
}.read(reader, element, record, "PropertyValue", "Annotation");
}
private void readFunction(XMLEventReader reader, StartElement element, CsdlSchema schema)
@ -1134,7 +1155,7 @@ public class MetadataParser {
}
abstract class ElementReader<T> {
void read(XMLEventReader reader, StartElement element, T t, String... names)
void read(XMLEventReader reader, StartElement parentElement, T t, String... names)
throws XMLStreamException {
while (reader.hasNext()) {
XMLEvent event = reader.peek();
@ -1153,11 +1174,17 @@ public class MetadataParser {
continue;
}
if (parentElement != null && event.isEndElement()
&& ((EndElement) event).getName().equals(parentElement.getName())) {
// end reached
break;
}
boolean hit = false;
for (String name : names) {
if (event.isStartElement()) {
element = event.asStartElement();
StartElement element = event.asStartElement();
if (element.getName().getLocalPart().equals(name)) {
reader.nextEvent(); // advance cursor start which is current
build(reader, element, t, name);

View File

@ -110,6 +110,9 @@ public class MetadataParserAnnotationsTest {
assertEquals(7, apply.getParameters().size());
assertTrue(apply.getParameters().get(1) instanceof CsdlPath);
assertTrue(apply.getParameters().get(4) instanceof CsdlConstantExpression);
assertEquals("OData.Description", apply.getAnnotations().get(0).getTerm());
assertEquals("concat apply", apply.getAnnotations().get(0).getExpression().asConstant().getValue());
}
@Test
@ -148,6 +151,10 @@ public class MetadataParserAnnotationsTest {
CsdlIsOf isOf = (CsdlIsOf)a.getExpression();
assertEquals("Self.PreferredCustomer", isOf.getType());
assertTrue(isOf.getValue() instanceof CsdlPath);
assertEquals("OData.Description", isOf.getAnnotations().get(0).getTerm());
assertEquals("preferred customer", isOf.getAnnotations().get(0).getExpression().asConstant().getValue());
}
@Test
@ -186,6 +193,8 @@ public class MetadataParserAnnotationsTest {
CsdlPropertyValue value = expr.getPropertyValues().get(0);
assertEquals("NonUpdatableNavigationProperties", value.getProperty());
assertTrue(value.getValue() instanceof CsdlCollection);
assertEquals("OData.Description", expr.getAnnotations().get(0).getTerm());
assertEquals("descripiton test", expr.getAnnotations().get(0).getExpression().asConstant().getValue());
CsdlCollection collection = (CsdlCollection)value.getValue();
assertEquals(2, collection.getItems().size());

View File

@ -24,7 +24,11 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import org.apache.olingo.commons.api.ex.ODataException;
@ -187,5 +191,26 @@ public class MetadataParserTest {
public void testParsingWithNoFormat() throws Exception {
MetadataParser parser = new MetadataParser();
provider = (CsdlEdmProvider) parser.buildEdmProvider(new FileReader("src/test/resources/skip-annotation.xml"));
}
}
@Test
public void testReferenceLoad() throws Exception {
MetadataParser parser = new MetadataParser();
parser.recursivelyLoadReferences(false);
parser.referenceResolver(new ReferenceResolver() {
@Override
public InputStream resolveReference(URI uri, String xmlBase) {
String str = uri.toASCIIString();
if (str.startsWith("http://localhost/")) {
try {
return new FileInputStream("src/test/resources/"+str.substring(17));
} catch (FileNotFoundException e) {
return null;
}
}
return null;
}
});
provider = (CsdlEdmProvider) parser.buildEdmProvider(new FileReader("src/test/resources/test.xml"));
}
}

View File

@ -0,0 +1,33 @@
<?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/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/>
</edmx:Reference>
<edmx:Reference Uri="http://localhost/b.xml">
<edmx:Include Namespace="org.apache.olingo.b" Alias="B"/>
</edmx:Reference>
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="org.apache.olingo.a">
<Term Name="ExtendedInfo" Type="Collection(Message.Message)" Nullable="false">
<Annotation Term="OData.Description" String="The ExtendedInfo annotation can be applied to any object or property to provide additional information about the item."/>
</Term>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@ -52,6 +52,7 @@
</String>
<Path>Available/Unit</Path>
<String> available)</String>
<Annotation Term="OData.Description" String="concat apply"/>
</Apply>
</Annotation>
@ -79,6 +80,7 @@
<Annotation Term="Self.IsPreferredCustomer">
<IsOf Type="Self.PreferredCustomer">
<Path>Customer</Path>
<Annotation Term="OData.Description" String="preferred customer"/>
</IsOf>
</Annotation>
@ -105,6 +107,7 @@
<NavigationPropertyPath>Category</NavigationPropertyPath>
</Collection>
</PropertyValue>
<Annotation Term="OData.Description" String="descripiton test"/>
</Record>
</Annotation>

View File

@ -0,0 +1,29 @@
<?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/errata03/csd01/complete/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Namespace="Org.OData.Core.V1" Alias="OData"/>
</edmx:Reference>
<edmx:Reference Uri="http://localhost/a.xml">
<edmx:Include Namespace="org.apache.olingo.a" Alias="A2"/>
</edmx:Reference>
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="org.apache.olingo.b">
<Term Name="ExtendedInfo" Type="Collection(Message.Message)" Nullable="false">
<Annotation Term="OData.Description" String="The ExtendedInfo annotation can be applied to any object or property to provide additional information about the item."/>
</Term>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@ -0,0 +1,26 @@
<?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 Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="http://localhost/a.xml">
<edmx:Include Alias="A" Namespace="org.apache.olingo.a" />
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="Microsoft.OData.SampleService.Models.TripPin"
xmlns="http://docs.oasis-open.org/odata/ns/edm">
<ComplexType Name="City">
<Property Name="CountryRegion" Type="Edm.String" Nullable="false" />
<Property Name="Name" Type="Edm.String" Nullable="false" />
<Property Name="Region" Type="Edm.String" Nullable="false" />
</ComplexType>
</Schema>
</edmx:DataServices>
</edmx:Edmx>