/* * $URL:https://secure.revolsys.com/svn/open.revolsys.com/GIS/trunk/src/main/java/com/revolsys/gis/format/saif/io/SaifSchemaReader.java $ * $Author:paul.austin@revolsys.com $ * $Date:2007-06-09 09:28:28 -0700 (Sat, 09 Jun 2007) $ * $Revision:265 $ * Copyright 2004-2005 Revolution Systems Inc. * * Licensed 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 com.revolsys.record.io.format.saif; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import com.revolsys.datatype.CollectionDataType; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.datatype.EnumerationDataType; import com.revolsys.datatype.SimpleDataType; import com.revolsys.io.PathName; import com.revolsys.record.Record; import com.revolsys.record.io.format.saif.util.CsnIterator; import com.revolsys.record.property.FieldProperties; import com.revolsys.record.property.RecordDefinitionProperty; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.record.schema.RecordDefinitionFactory; import com.revolsys.record.schema.RecordDefinitionFactoryImpl; import com.revolsys.record.schema.RecordDefinitionImpl; import com.revolsys.spring.resource.Resource; public class SaifSchemaReader { private static final Map<String, DataType> nameTypeMap = new HashMap<>(); private static final String SPATIAL_OBJECT = "/SpatialObject"; private static final String TEXT_OR_SYMBOL_OBJECT = "/TextOrSymbolObject"; static { addType("/Boolean", DataTypes.BOOLEAN); addType("/Numeric", DataTypes.DECIMAL); addType("/Integer", DataTypes.INTEGER); addType("/Integer8", DataTypes.BYTE); addType("/Integer16", DataTypes.SHORT); addType("/Integer32", DataTypes.INT); addType("/Integer64", DataTypes.LONG); addType("/Integer8Unsigned", DataTypes.INTEGER); addType("/Integer16Unsigned", DataTypes.INTEGER); addType("/Integer32Unsigned", DataTypes.INTEGER); addType("/Integer64Unsigned", DataTypes.INTEGER); addType("/Real", DataTypes.DECIMAL); addType("/Real32", DataTypes.FLOAT); addType("/Real64", DataTypes.DOUBLE); addType("/Real80", DataTypes.DECIMAL); addType("/List", DataTypes.LIST); addType("/Set", DataTypes.SET); addType("/AggregateType", new SimpleDataType("AggregateType", Object.class)); addType("/PrimitiveType", new SimpleDataType("PrimitiveType", Object.class)); addType("/Enumeration", new SimpleDataType("Enumeration", Object.class)); } private static void addType(final String typePath, final DataType dataType) { nameTypeMap.put(String.valueOf(typePath), dataType); } private List<RecordDefinitionProperty> commonRecordDefinitionProperties = new ArrayList<>(); private RecordDefinitionImpl currentClass; private final Set<RecordDefinition> currentSuperClasses = new LinkedHashSet<>(); private RecordDefinitionFactoryImpl schema; private void addExportedObjects() { final RecordDefinitionImpl exportedObjectHandle = new RecordDefinitionImpl( PathName.newPathName("/ExportedObjectHandle")); this.schema.addRecordDefinition(exportedObjectHandle); exportedObjectHandle.addField("referenceID", DataTypes.STRING, true); exportedObjectHandle.addField("type", DataTypes.STRING, true); exportedObjectHandle.addField("offset", DataTypes.INTEGER, true); exportedObjectHandle.addField("sharable", DataTypes.BOOLEAN, true); } public void addSuperClass(final RecordDefinitionImpl currentClass, final RecordDefinition superClass) { currentClass.addSuperClass(superClass); for (final String name : superClass.getFieldNames()) { final FieldDefinition attribute = superClass.getField(name); currentClass.addField(attribute.clone()); } for (final Entry<String, Object> defaultValue : superClass.getDefaultValues().entrySet()) { final String name = defaultValue.getKey(); final Object value = defaultValue.getValue(); if (!currentClass.hasField(name)) { currentClass.addDefaultValue(name, value); } } final String idFieldName = superClass.getIdFieldName(); if (idFieldName != null) { currentClass.setIdFieldName(idFieldName); } String geometryFieldName = superClass.getGeometryFieldName(); final String path = currentClass.getPath(); if (path.equals("/TRIM/TrimText")) { geometryFieldName = "textOrSymbol"; } if (geometryFieldName != null) { currentClass.setGeometryFieldName(geometryFieldName); } } public void attributes(final RecordDefinition type, final CsnIterator iterator) throws IOException { while (iterator.getNextEventType() == CsnIterator.ATTRIBUTE_NAME || iterator.getNextEventType() == CsnIterator.OPTIONAL_ATTRIBUTE) { boolean required = true; switch (iterator.next()) { case CsnIterator.OPTIONAL_ATTRIBUTE: required = false; iterator.next(); case CsnIterator.ATTRIBUTE_NAME: final String fieldName = iterator.getStringValue(); switch (iterator.next()) { case CsnIterator.ATTRIBUTE_TYPE: final String typePath = iterator.getPathValue(); DataType dataType = nameTypeMap.get(typePath); if (typePath.equals(SPATIAL_OBJECT) || typePath.equals(TEXT_OR_SYMBOL_OBJECT)) { dataType = DataTypes.GEOMETRY; this.currentClass.setGeometryFieldIndex(this.currentClass.getFieldCount()); } else if (dataType == null) { dataType = new SimpleDataType(typePath, Record.class); } this.currentClass.addField(fieldName, dataType, required); break; case CsnIterator.COLLECTION_ATTRIBUTE: final String collectionType = iterator.getPathValue(); if (iterator.next() == CsnIterator.CLASS_NAME) { final String contentTypeName = iterator.getPathValue(); final DataType collectionDataType = nameTypeMap.get(collectionType); DataType contentDataType = nameTypeMap.get(contentTypeName); if (contentDataType == null) { contentDataType = DataTypes.RECORD; } this.currentClass.addField(fieldName, new CollectionDataType(collectionDataType.getName(), collectionDataType.getJavaClass(), contentDataType), required); } else { throw new IllegalStateException("Expecting attribute type"); } break; case CsnIterator.STRING_ATTRIBUTE: int length = Integer.MAX_VALUE; if (iterator.getEventType() == CsnIterator.STRING_ATTRIBUTE_LENGTH) { length = iterator.getIntegerValue(); } this.currentClass.addField(fieldName, DataTypes.STRING, length, required); break; default: throw new IllegalStateException("Unknown event type: " + iterator.getEventType()); } break; default: break; } } } public void classAttributes(final RecordDefinition type, final CsnIterator iterator) throws IOException { } public void comments(final RecordDefinition type, final CsnIterator iterator) throws IOException { if (iterator.next() == CsnIterator.VALUE) { iterator.getStringValue(); } } public void defaults(final RecordDefinition type, final CsnIterator iterator) throws IOException { while (iterator.getNextEventType() == CsnIterator.ATTRIBUTE_PATH) { iterator.next(); final String fieldName = iterator.getStringValue(); if (iterator.next() == CsnIterator.VALUE) { final Object value = iterator.getValue(); this.currentClass.addDefaultValue(fieldName, value); } else { throw new IllegalStateException("Expecting a value"); } } } public List<RecordDefinitionProperty> getCommonRecordDefinitionProperties() { return this.commonRecordDefinitionProperties; } private Object getDefinition(final CsnIterator iterator) throws IOException { while (iterator.next() != CsnIterator.END_DEFINITION) { switch (iterator.getEventType()) { case CsnIterator.CLASS_NAME: final String superClassName = iterator.getPathValue(); if (superClassName.equals("/Enumeration")) { final DataType enumeration = processEnumeration(iterator); nameTypeMap.put(enumeration.getName(), enumeration); return enumeration; } final RecordDefinition superClass = this.schema.getRecordDefinition(superClassName); if (superClass == null) { throw new IllegalStateException("Cannot find super class '" + superClassName + "'"); } this.currentSuperClasses.add(superClass); break; case CsnIterator.COMPONENT_NAME: final String componentName = iterator.getStringValue(); try { final Method method = getClass().getMethod(componentName, new Class[] { RecordDefinition.class, CsnIterator.class }); method.invoke(this, new Object[] { this.currentClass, iterator }); } catch (final SecurityException e) { throw new IllegalStateException("Unknown component '" + componentName + "'"); } catch (final NoSuchMethodException e) { throw new IllegalStateException("Unknown component '" + componentName + "'"); } catch (final IllegalAccessException e) { throw new RuntimeException(e.getMessage(), e); } catch (final InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException)cause; } else if (cause instanceof Error) { throw (Error)cause; } else if (cause instanceof IOException) { throw (IOException)cause; } else { throw new RuntimeException(cause.getMessage(), cause); } } default: break; } } return this.currentClass; } private RecordDefinitionFactory loadSchema(final CsnIterator iterator) throws IOException { if (this.schema == null) { this.schema = new RecordDefinitionFactoryImpl(); this.schema .addRecordDefinition(new RecordDefinitionImpl(PathName.newPathName("/AggregateType"))); this.schema .addRecordDefinition(new RecordDefinitionImpl(PathName.newPathName("/PrimitiveType"))); addExportedObjects(); } while (iterator.next() != CsnIterator.END_DOCUMENT) { this.currentSuperClasses.clear(); this.currentClass = null; final Object definition = getDefinition(iterator); if (definition instanceof RecordDefinition) { final RecordDefinitionImpl recordDefinition = (RecordDefinitionImpl)definition; setRecordDefinitionProperties(recordDefinition); recordDefinition.setRecordDefinitionFactory(this.schema); this.schema.addRecordDefinition(recordDefinition); } } return this.schema; } public RecordDefinitionFactory loadSchema(final File file) throws IOException { final CsnIterator iterator = new CsnIterator(file); return loadSchema(iterator); } public RecordDefinitionFactory loadSchema(final Resource resource) throws IOException { return loadSchema(new CsnIterator(resource.getFilename(), resource.getInputStream())); } public RecordDefinitionFactory loadSchema(final String fileName, final InputStream in) throws IOException { return loadSchema(new CsnIterator(fileName, in)); } public RecordDefinitionFactory loadSchemas(final List<Resource> resources) throws IOException { for (final Resource resource : resources) { if (resource.exists()) { loadSchema(resource); } } return this.schema; } private DataType processEnumeration(final CsnIterator iterator) throws IOException { String name = null; final Set<String> allowedValues = new TreeSet<>(); while (iterator.getNextEventType() == CsnIterator.COMPONENT_NAME) { iterator.next(); final String componentName = iterator.getStringValue(); if (componentName.equals("subclass")) { if (iterator.next() == CsnIterator.CLASS_NAME) { name = iterator.getPathValue(); } else { throw new IllegalArgumentException("Expecting an enumeration class name"); } } else if (componentName.equals("values")) { while (iterator.getNextEventType() == CsnIterator.TAG_NAME) { iterator.next(); final String tagName = iterator.getStringValue(); allowedValues.add(tagName); } } else if (!componentName.equals("comments")) { throw new IllegalArgumentException( "Unknown component " + componentName + " for enumberation " + name); } } return new EnumerationDataType(name, String.class, allowedValues); } public void restricted(final RecordDefinition type, final CsnIterator iterator) throws IOException { while (iterator.getNextEventType() == CsnIterator.ATTRIBUTE_PATH) { iterator.next(); String fieldName = iterator.getStringValue(); boolean hasMore = true; final List<String> typePaths = new ArrayList<>(); final List<Object> values = new ArrayList<>(); while (hasMore) { switch (iterator.getNextEventType()) { case CsnIterator.CLASS_NAME: iterator.next(); final String typePath = iterator.getPathValue(); typePaths.add(typePath); break; case CsnIterator.FORCE_TYPE: iterator.next(); if (iterator.next() == CsnIterator.CLASS_NAME) { typePaths.add(iterator.getPathValue()); } else { throw new IllegalStateException("Expecting a class name"); } break; case CsnIterator.EXCLUDE_TYPE: iterator.next(); if (iterator.next() == CsnIterator.CLASS_NAME) { typePaths.add(iterator.getPathValue()); } else { throw new IllegalStateException("Expecting a class name"); } break; case CsnIterator.VALUE: iterator.next(); values.add(iterator.getValue()); break; default: hasMore = false; break; } } fieldName = fieldName.replaceFirst("position.geometry", "position"); final int dotIndex = fieldName.indexOf('.'); if (dotIndex == -1) { final FieldDefinition attribute = type.getField(fieldName); if (attribute != null) { if (!typePaths.isEmpty()) { attribute.setProperty(FieldProperties.ALLOWED_TYPE_NAMES, typePaths); } if (!values.isEmpty()) { attribute.setProperty(FieldProperties.ALLOWED_VALUES, values); } } } else { final String key = fieldName.substring(0, dotIndex); final String subKey = fieldName.substring(dotIndex + 1); final FieldDefinition attribute = type.getField(key); if (attribute != null) { if (!typePaths.isEmpty()) { Map<String, List<String>> allowedValues = attribute .getProperty(FieldProperties.FIELD_ALLOWED_TYPE_NAMES); if (allowedValues == null) { allowedValues = new HashMap<>(); attribute.setProperty(FieldProperties.FIELD_ALLOWED_TYPE_NAMES, allowedValues); } allowedValues.put(subKey, typePaths); } if (!values.isEmpty()) { Map<String, List<Object>> allowedValues = attribute .getProperty(FieldProperties.FIELD_ALLOWED_VALUES); if (allowedValues == null) { allowedValues = new HashMap<>(); attribute.setProperty(FieldProperties.FIELD_ALLOWED_VALUES, allowedValues); } allowedValues.put(subKey, values); } } } } } public void setCommonRecordDefinitionProperties( final List<RecordDefinitionProperty> commonRecordDefinitionProperties) { this.commonRecordDefinitionProperties = commonRecordDefinitionProperties; } private void setRecordDefinitionProperties(final RecordDefinitionImpl recordDefinition) { for (final RecordDefinitionProperty property : this.commonRecordDefinitionProperties) { final RecordDefinitionProperty clonedProperty = property.clone(); clonedProperty.setRecordDefinition(recordDefinition); } } public void subclass(final RecordDefinition type, final CsnIterator iterator) throws IOException { if (iterator.next() == CsnIterator.CLASS_NAME) { final String className = iterator.getPathValue(); this.currentClass = new RecordDefinitionImpl(PathName.newPathName(className)); for (final RecordDefinition superClassDef : this.currentSuperClasses) { addSuperClass(this.currentClass, superClassDef); } // currentClass.setName(className); this.schema.addRecordDefinition(this.currentClass); } } }