Skip to content
Permalink
Browse files

Add Core API support for array fields with base type EnumValue.

  • Loading branch information...
archiecobbs committed Feb 17, 2019
1 parent 3210d5e commit 2812956d4fdd566720edfcec945d433e9c3f22d9
Showing with 532 additions and 164 deletions.
  1. +78 −0 permazen-coreapi/src/main/java/io/permazen/core/EnumArrayField.java
  2. +1 −1 permazen-coreapi/src/main/java/io/permazen/core/EnumField.java
  3. +15 −0 permazen-coreapi/src/main/java/io/permazen/core/FieldBuilder.java
  4. +8 −0 permazen-coreapi/src/main/java/io/permazen/core/FieldSwitch.java
  5. +11 −0 permazen-coreapi/src/main/java/io/permazen/core/FieldSwitchAdapter.java
  6. +186 −0 permazen-coreapi/src/main/java/io/permazen/schema/AbstractEnumSchemaField.java
  7. +137 −0 permazen-coreapi/src/main/java/io/permazen/schema/EnumArraySchemaField.java
  8. +3 −159 permazen-coreapi/src/main/java/io/permazen/schema/EnumSchemaField.java
  9. +8 −0 permazen-coreapi/src/main/java/io/permazen/schema/SchemaFieldSwitch.java
  10. +12 −0 permazen-coreapi/src/main/java/io/permazen/schema/SchemaFieldSwitchAdapter.java
  11. +14 −2 permazen-coreapi/src/main/java/io/permazen/schema/SchemaModel.java
  12. +4 −0 permazen-coreapi/src/main/java/io/permazen/schema/SchemaObjectType.java
  13. +2 −0 permazen-coreapi/src/main/java/io/permazen/schema/XMLConstants.java
  14. +18 −1 permazen-coreapi/src/test/java/io/permazen/core/util/XMLObjectSerializerTest.java
  15. +19 −1 permazen-coreapi/src/test/java/io/permazen/schema/LockDownTest.java
  16. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test1.xml
  17. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test2.xml
  18. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test2a.xml
  19. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test2b.xml
  20. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test3.xml
  21. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test4.xml
  22. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test4b.xml
  23. +2 −0 permazen-coreapi/src/test/resources/io/permazen/core/util/test4c.xml
@@ -0,0 +1,78 @@

/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/

package io.permazen.core;

import com.google.common.base.Preconditions;

import io.permazen.core.type.ArrayType;
import io.permazen.core.type.EnumFieldType;
import io.permazen.core.type.NullSafeType;

/**
* A field that contains an array (having one or more dimensions) of values chosen from
* an ordered list of unique {@link String} identifiers.
*
* <p>
* Two instances of this class are considered compatible only when their ordered lists of identifiers and dimensions are identical.
*/
public class EnumArrayField extends SimpleField<Object> {

private final EnumFieldType baseType;
private final int dimensions;

/**
* Constructor.
*
* @param name the name of the field
* @param storageId field storage ID
* @param schema schema version
* @param indexed whether this field is indexed
* @param baseType base component type
* @param fieldType field type
* @param dimensions number of dimensions
* @throws IllegalArgumentException if any parameter is null
* @throws IllegalArgumentException if {@code name} is invalid
* @throws IllegalArgumentException if {@code storageId} is invalid
* @throws IllegalArgumentException if {@code dimensions} is invalid
*/
@SuppressWarnings("unchecked")
EnumArrayField(String name, int storageId, Schema schema,
boolean indexed, EnumFieldType baseType, FieldType<?> fieldType, int dimensions) {
super(name, storageId, schema, (FieldType<Object>)fieldType, indexed);
Preconditions.checkArgument(dimensions >= 1 && dimensions <= ArrayType.MAX_DIMENSIONS);
this.baseType = baseType;
this.dimensions = dimensions;
assert this.dimensions == ((ArrayType<?, ?>)((NullSafeType<?>)fieldType).getInnerType()).getDimensions();
}

/**
* Get the base field type.
*/
public EnumFieldType getBaseType() {
return this.baseType;
}

/**
* Get the number of enum array dimensions.
*
* @return number of dimensions, a value from 1 to 255
*/
public int getDimensions() {
return this.dimensions;
}

// Public methods

@Override
public <R> R visit(FieldSwitch<R> target) {
return target.caseEnumArrayField(this);
}

@Override
public String toString() {
return "enum array field `" + this.name + "'";
}
}
@@ -10,7 +10,7 @@
import java.util.List;

/**
* A field that contains one entry in an ordered list of unique {@link String} identifiers.
* A field that contains a value chosen from in an ordered list of unique {@link String} identifiers.
*
* <p>
* Two instances of this class are considered compatible only when their ordered lists of identifiers are identical.
@@ -7,7 +7,10 @@

import com.google.common.base.Preconditions;

import io.permazen.core.type.ArrayType;
import io.permazen.core.type.EnumFieldType;
import io.permazen.schema.CounterSchemaField;
import io.permazen.schema.EnumArraySchemaField;
import io.permazen.schema.EnumSchemaField;
import io.permazen.schema.ListSchemaField;
import io.permazen.schema.MapSchemaField;
@@ -91,6 +94,18 @@ public EnumField caseEnumSchemaField(EnumSchemaField field) {
return new EnumField(field.getName(), field.getStorageId(), this.schema, field.isIndexed(), field.getIdentifiers());
}

@Override
public EnumArrayField caseEnumArraySchemaField(EnumArraySchemaField field) {
Preconditions.checkArgument(field.getEncodingSignature() == 0, "encoding signature must be zero");
Preconditions.checkArgument(field.getDimensions() >= 1 && field.getDimensions() <= ArrayType.MAX_DIMENSIONS);
final EnumFieldType baseType = new EnumFieldType(field.getIdentifiers());
FieldType<?> fieldType = baseType;
for (int dims = 0; dims < field.getDimensions(); dims++)
fieldType = this.fieldTypeRegistry.getArrayType(fieldType);
return new EnumArrayField(field.getName(), field.getStorageId(),
this.schema, field.isIndexed(), baseType, fieldType, field.getDimensions());
}

@Override
public CounterField caseCounterSchemaField(CounterSchemaField field) {
return new CounterField(field.getName(), field.getStorageId(), this.schema);
@@ -73,5 +73,13 @@
* @return visitor return value
*/
R caseEnumField(EnumField field);

/**
* Handle an {@link EnumArrayField}.
*
* @param field visiting field
* @return visitor return value
*/
R caseEnumArrayField(EnumArrayField field);
}

@@ -91,6 +91,17 @@ public R caseEnumField(EnumField field) {
return this.caseSimpleField(field);
}

/**
* Handle a {@link EnumArrayField}.
*
* <p>
* The implementation in {@link FieldSwitchAdapter} delegates to {@link #caseSimpleField caseSimpleField()}.
*/
@Override
public R caseEnumArrayField(EnumArrayField field) {
return this.caseSimpleField(field);
}

/**
* Adapter class roll-up method.
*
@@ -0,0 +1,186 @@

/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/

package io.permazen.schema;

import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;

import io.permazen.core.InvalidSchemaException;
import io.permazen.core.type.EnumFieldType;
import io.permazen.util.Diffs;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeMap;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.dellroad.stuff.string.StringEncoder;

/**
* Superclass of {@link SimpleSchemaField} types involving {@link EnumValue}s representing {@link Enum} types.
*/
public abstract class AbstractEnumSchemaField extends SimpleSchemaField {

private /*final*/ List<String> idents = new ArrayList<>();

/**
* Get the ordered list of identifiers that constitute the enum type.
*
* @return enum identifier list
*/
public List<String> getIdentifiers() {
return this.idents;
}

// Lockdown

@Override
void lockDownRecurse() {
super.lockDownRecurse();
this.idents = Collections.unmodifiableList(this.idents);
}

// Validation

@Override
void validate() {
super.validate();
if (this.getEncodingSignature() != 0)
throw new IllegalArgumentException("invalid " + this + ": encoding signature must be zero");
try {
EnumFieldType.validateIdentifiers(this.idents);
} catch (IllegalArgumentException e) {
throw new InvalidSchemaException("invalid " + this + ": " + e.getMessage(), e);
}
}

@Override
void validateType() {
// we ignore the type
}

// Compatibility

// For enum types, we don't care if the type names are different; this allows enum types
// to change their Java class or packge names without creating an incompatible schema.
@Override
boolean isCompatibleType(SimpleSchemaField field) {
final AbstractEnumSchemaField that = (AbstractEnumSchemaField)field;
return this.idents.equals(that.idents);
}

@Override
void writeFieldTypeCompatibilityHashData(DataOutputStream output) throws IOException {
output.writeInt(this.idents.size());
for (String ident : this.idents)
output.writeUTF(ident);
}

// XML Reading

@Override
void readSubElements(XMLStreamReader reader, int formatVersion) throws XMLStreamException {
while (this.expect(reader, true, XMLConstants.IDENTIFIER_TAG))
this.idents.add(reader.getElementText());
}

// XML Writing

@Override
void writeXML(XMLStreamWriter writer, boolean includeName) throws XMLStreamException {
this.writeElement(writer, includeName);
this.writeAttributes(writer, includeName);
for (String ident : this.idents) {
writer.writeStartElement(XMLConstants.IDENTIFIER_TAG.getNamespaceURI(), XMLConstants.IDENTIFIER_TAG.getLocalPart());
writer.writeCharacters(StringEncoder.encode(ident, false));
writer.writeEndElement();
}
writer.writeEndElement();
}

@Override
void writeTypeAttribute(XMLStreamWriter writer) throws XMLStreamException {
// we ignore the type
}

abstract void writeElement(XMLStreamWriter writer, boolean includeName) throws XMLStreamException;

// DiffGenerating

@Override
public Diffs differencesFrom(SimpleSchemaField other) {
final Diffs diffs = new Diffs(super.differencesFrom(other));
if (!(other instanceof AbstractEnumSchemaField)) {
diffs.add("change type from " + other.getClass().getSimpleName() + " to " + this.getClass().getSimpleName());
return diffs;
}
final AbstractEnumSchemaField that = (AbstractEnumSchemaField)other;
if (!this.idents.equals(that.idents)) {
final Diffs enumDiffs = new Diffs();
final TreeMap<String, Integer> thisOrdinals = new TreeMap<>();
final TreeMap<String, Integer> thatOrdinals = new TreeMap<>();
for (int i = 0; i < this.idents.size(); i++)
thisOrdinals.put(this.idents.get(i), i);
for (int i = 0; i < that.idents.size(); i++)
thatOrdinals.put(that.idents.get(i), i);
final PeekingIterator<String> thisIterator = Iterators.peekingIterator(thisOrdinals.keySet().iterator());
final PeekingIterator<String> thatIterator = Iterators.peekingIterator(thatOrdinals.keySet().iterator());
while (thisIterator.hasNext() || thatIterator.hasNext()) {
final String thisName = thisIterator.hasNext() ? thisIterator.peek() : null;
final String thatName = thatIterator.hasNext() ? thatIterator.peek() : null;
assert thisName != null || thatName != null;
final int diff = thisName == null ? 1 : thatName == null ? -1 : thisName.compareTo(thatName);
if (diff < 0)
enumDiffs.add("added `" + thisName + "' (ordinal " + thisOrdinals.get(thisName) + ")");
else
thatIterator.next();
if (diff > 0)
enumDiffs.add("removed `" + thatName + "' (ordinal " + thatOrdinals.get(thatName) + ")");
else
thisIterator.next();
}
diffs.add("changed enum identifier list", enumDiffs);
}
return diffs;
}

@Override
void addTypeDifference(Diffs diffs, SimpleSchemaField that) {
// we ignore the type
}

// Object

@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!super.equals(obj))
return false;
final AbstractEnumSchemaField that = (AbstractEnumSchemaField)obj;
return this.idents.equals(that.idents);
}

@Override
public int hashCode() {
return super.hashCode() ^ this.idents.hashCode();
}

// Cloneable

@Override
public AbstractEnumSchemaField clone() {
final AbstractEnumSchemaField clone = (AbstractEnumSchemaField)super.clone();
clone.idents = new ArrayList<>(this.idents);
return clone;
}
}
Oops, something went wrong.

0 comments on commit 2812956

Please sign in to comment.
You can’t perform that action at this time.