Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance AppSchemaMapper by reference data #1111

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ public class AppSchemaMapper {

private final int maxComplexityIndex;

private final int allowedCycleDepth;

private final ReferenceData referenceData;

private final int allowedCycleDepth;

/**
* Creates a new {@link AppSchemaMapper} instance for the given schema.
Expand Down Expand Up @@ -184,11 +184,40 @@ public AppSchemaMapper( AppSchema appSchema, boolean createBlobMapping, boolean
public AppSchemaMapper( AppSchema appSchema, boolean createBlobMapping, boolean createRelationalMapping,
GeometryStorageParams geometryParams, int maxLength, boolean usePrefixedSQLIdentifiers,
boolean useIntegerFids, int allowedCycleDepth ) {
this(appSchema, createBlobMapping, createRelationalMapping, geometryParams, maxLength,
usePrefixedSQLIdentifiers, useIntegerFids, allowedCycleDepth, null );
}
/**
* Creates a new {@link AppSchemaMapper} instance for the given schema.
*
* @param appSchema
* application schema to be mapped, must not be <code>null</code>
* @param createBlobMapping
* true, if BLOB mapping should be performed, false otherwise
* @param createRelationalMapping
* true, if relational mapping should be performed, false otherwise
* @param geometryParams
* parameters for storing geometries, must not be <code>null</code>
* @param maxLength
* max length of column names
* @param usePrefixedSQLIdentifiers
* <code>true</code> if the sql identifiers should be prefixed, <code>false</code> otherwise
* @param useIntegerFids
* <code>true</code> if the integer fids should be used, <code>false</code> for uuids
* @param allowedCycleDepth
* depth of the allowed cycles
* @param referenceData
* describing the data stored in the features store
*/
public AppSchemaMapper( AppSchema appSchema, boolean createBlobMapping, boolean createRelationalMapping,
GeometryStorageParams geometryParams, int maxLength, boolean usePrefixedSQLIdentifiers,
boolean useIntegerFids, int allowedCycleDepth, ReferenceData referenceData ) {
this.appSchema = appSchema;
this.geometryParams = geometryParams;
this.useIntegerFids = useIntegerFids;
this.allowedCycleDepth = allowedCycleDepth;
this.maxComplexityIndex = DEFAULT_COMPLEXITY_INDEX * ( allowedCycleDepth + 1 );
this.referenceData = referenceData;

List<FeatureType> ftList = appSchema.getFeatureTypes( null, false, false );
List<FeatureType> blackList = new ArrayList<FeatureType>();
Expand Down Expand Up @@ -262,7 +291,7 @@ private FeatureTypeMapping[] generateFtMappings( FeatureType[] fts ) {
}

private FeatureTypeMapping generateFtMapping( FeatureType ft ) {
CycleAnalyser cycleAnalyser = new CycleAnalyser( allowedCycleDepth );
CycleAnalyser cycleAnalyser = new CycleAnalyser( allowedCycleDepth, ft.getName() );
LOG.info( "Mapping feature type '" + ft.getName() + "'" );
MappingContext mc = mcManager.newContext( ft.getName(), detectPrimaryKeyColumnName() );

Expand Down Expand Up @@ -318,7 +347,7 @@ private List<Mapping> generatePropMapping( PropertyType pt, MappingContext mc, C

MappingContext propMc = null;
List<TableJoin> jc = null;
if ( pt.getMaxOccurs() == 1 ) {
if ( pt.getMaxOccurs() == 1 || referenceDataHasOnlyOne( cycleAnalyser ) ) {
propMc = mcManager.mapOneToOneElement( mc, eName );
} else {
propMc = mcManager.mapOneToManyElements( mc, eName );
Expand Down Expand Up @@ -354,7 +383,7 @@ private List<Mapping> generatePropMapping( PropertyType pt, MappingContext mc, C
}

if ( pt instanceof SimplePropertyType ) {
mappings.add( generatePropMapping( (SimplePropertyType) pt, mc ) );
mappings.add( generatePropMapping( (SimplePropertyType) pt, mc, cycleAnalyser ) );
} else if ( pt instanceof GeometryPropertyType ) {
mappings.add( generatePropMapping( (GeometryPropertyType) pt, mc ) );
} else if ( pt instanceof FeaturePropertyType ) {
Expand All @@ -381,13 +410,13 @@ private List<Mapping> generatePropMapping( PropertyType pt, MappingContext mc, C
return mappings;
}

private PrimitiveMapping generatePropMapping( SimplePropertyType pt, MappingContext mc ) {
private PrimitiveMapping generatePropMapping( SimplePropertyType pt, MappingContext mc, CycleAnalyser cycleAnalyser ) {
LOG.debug( "Mapping simple property '" + pt.getName() + "'" );
ValueReference path = getPropName( pt.getName() );
MappingContext propMc = null;
List<TableJoin> jc = null;
MappingExpression mapping = null;
if ( pt.getMaxOccurs() == 1 ) {
if ( pt.getMaxOccurs() == 1 || referenceDataHasOnlyOne( cycleAnalyser ) ) {
propMc = mcManager.mapOneToOneElement( mc, pt.getName() );
mapping = new DBField( propMc.getColumn() );
} else {
Expand Down Expand Up @@ -468,7 +497,7 @@ private CompoundMapping generatePropMapping( CustomPropertyType pt, MappingConte

MappingContext propMc = null;
List<TableJoin> jc = null;
if ( pt.getMaxOccurs() == 1 ) {
if ( pt.getMaxOccurs() == 1 || referenceDataHasOnlyOne( cycleAnalyser ) ) {
propMc = mcManager.mapOneToOneElement( mc, pt.getName() );
} else {
propMc = mcManager.mapOneToManyElements( mc, pt.getName() );
Expand Down Expand Up @@ -714,6 +743,8 @@ private List<Mapping> generateMapping( XSTerm term, int occurence, MappingContex
private List<Mapping> generateMapping( XSElementDeclaration elDecl, int occurence, MappingContext mc,
CycleAnalyser cycleAnalyser ) {
cycleAnalyser.add( elDecl );
if ( referenceDataHasOnlyOne( cycleAnalyser ) )
occurence = 1;

List<Mapping> mappings = new ArrayList<Mapping>();

Expand Down Expand Up @@ -818,6 +849,14 @@ private List<Mapping> generateMapping( XSWildcard wildCard, int occurrence, Mapp
return new ArrayList<>();
}

private boolean referenceDataHasOnlyOne( CycleAnalyser cycleAnalyser ) {
if ( referenceData == null )
return false;
List<QName> xpath = cycleAnalyser.getPath();
QName featureTypeName = cycleAnalyser.getFeatureTypeName();
return referenceData.hasZeroOrOneProperty( featureTypeName, xpath );
}

private String getName( QName name ) {
if ( name.getNamespaceURI() != null && !name.getNamespaceURI().equals( "" ) ) {
String prefix = nsToPrefix.get( name.getNamespaceURI() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ public class CycleAnalyser {

private final int allowedCycleDepth;

private final QName featureTypeName;

/**
* @param allowedCycleDepth
* the allowed depth of cycles
* @param featureTypeName name of the feature type, never <code>null</code>
*/
CycleAnalyser( int allowedCycleDepth ) {
CycleAnalyser( int allowedCycleDepth, QName featureTypeName ) {
this.allowedCycleDepth = allowedCycleDepth;
this.featureTypeName = featureTypeName;
}

/**
Expand Down Expand Up @@ -141,6 +145,20 @@ public List<XSElementDeclaration> getElementDeclarations() {
return parentEls;
}

/**
* @return the name of the feature type, never <code>null</code>
*/
public QName getFeatureTypeName() {
return featureTypeName;
}

/**
* @return the current path. May be empty but never <code>null</code>
*/
public List<QName> getPath() {
return path;
}

private void log() {
StringBuffer sb = new StringBuffer();
Map<QName, Integer> nameToCycleDepth = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.deegree.feature.persistence.sql.mapper;

import org.deegree.commons.tom.ElementNode;
import org.deegree.commons.tom.TypedObjectNode;
import org.deegree.commons.tom.gml.property.Property;
import org.deegree.cs.exceptions.UnknownCRSException;
import org.deegree.feature.Feature;
import org.deegree.feature.FeatureCollection;
import org.deegree.gml.GMLInputFactory;
import org.deegree.gml.GMLStreamReader;
import org.deegree.gml.GMLVersion;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a>
*/
public class GmlReferenceData implements ReferenceData {

private Map<QName, List<Feature>> features;

public GmlReferenceData( URL referenceData )
throws IOException, XMLStreamException, UnknownCRSException {
GMLStreamReader gmlStreamReader = GMLInputFactory.createGMLStreamReader( GMLVersion.GML_32, referenceData );
FeatureCollection featureCollection = gmlStreamReader.readFeatureCollection();
this.features = featureCollection.stream().collect( Collectors.groupingBy( Feature::getName ) );
}

@Override
public boolean hasZeroOrOneProperty( QName featureTypeName, List<QName> xpath ) {
List<Feature> featuresOfType = this.features.get( featureTypeName );
if ( featuresOfType != null && !featuresOfType.isEmpty() ) {
for ( Feature feature : featuresOfType ) {
if ( hasMoreThanOne( feature, xpath ) )
return false;
}
return true;
}
return false;
}

private boolean hasMoreThanOne( Feature feature, List<QName> xpath ) {
if ( xpath.isEmpty() )
return true;
Iterator<QName> iterator = xpath.iterator();
QName firstProperty = iterator.next();
List<Property> properties = feature.getProperties( firstProperty );
return hasMoreThanOne( iterator, properties );
}

private <T extends ElementNode> boolean hasMoreThanOne( Iterator<QName> iterator, List<T> properties ) {
if ( !iterator.hasNext() ) {
if ( properties.size() > 1 )
return true;
else
return false;
} else {
QName next = iterator.next();
for ( ElementNode property : properties ) {
List<ElementNode> subProperties = getChildsByName( property, next );
if ( hasMoreThanOne( iterator, subProperties ) )
return true;
}
return false;
}
}

private List<ElementNode> getChildsByName( ElementNode property, QName propertyName ) {
List<ElementNode> properties = new ArrayList<>();
List<TypedObjectNode> children = property.getChildren();
for ( TypedObjectNode child : children ) {
if ( child instanceof ElementNode ) {
QName name = ( (ElementNode) child ).getName();
if ( name.equals( propertyName ) )
properties.add( (ElementNode) child );
}
}
return properties;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.deegree.feature.persistence.sql.mapper;

import javax.xml.namespace.QName;
import java.util.List;

/**
* @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a>
*/
public interface ReferenceData {

/**
* @param featureTypeName
* the name of the feature type, never <code>null</code>
* @param xpath
* the steps describing the path to the feature, may be empty. but never <code>null</code>
* @return <code>true</code> if the property identified by the path occurs one or zero times, <code>false</code> otherwise
*/
boolean hasZeroOrOneProperty( QName featureTypeName, List<QName> xpath );

}
Loading