Commit 7a33946d by Harish Butani

add support for Traits

parent 52460fa9
/**
* 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.
*/
package org.apache.metadata.storage;
import org.apache.metadata.IStruct;
import org.apache.metadata.MetadataException;
import org.apache.metadata.types.DownCastFieldMapping;
public class DownCastStructInstance implements IStruct {
public final String typeName;
public final DownCastFieldMapping fieldMapping;
public final IStruct backingInstance;
public DownCastStructInstance(String typeName, DownCastFieldMapping fieldMapping,
IStruct backingInstance) {
this.typeName = typeName;
this.fieldMapping = fieldMapping;
this.backingInstance = backingInstance;
}
@Override
public String getTypeName() {
return typeName;
}
@Override
public Object get(String attrName) throws MetadataException {
return fieldMapping.get(this, attrName);
}
@Override
public void set(String attrName, Object val) throws MetadataException {
fieldMapping.set(this, attrName, val);
}
}
......@@ -28,6 +28,7 @@ import org.apache.metadata.types.TypeUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Map;
public class StructInstance implements IStruct {
public final String dataTypeName;
......@@ -100,9 +101,11 @@ public class StructInstance implements IStruct {
}
TypeUtils.outputVal("\n", buf, "");
String fieldPrefix = prefix + "\t";
for(AttributeInfo i : fieldMapping.fields.values()) {
Object aVal = s.get(i.name);
TypeUtils.outputVal(i.name + " : ", buf, fieldPrefix);
for(Map.Entry<String,AttributeInfo> e : fieldMapping.fields.entrySet()) {
String attrName = e.getKey();
AttributeInfo i = e.getValue();
Object aVal = s.get(attrName);
TypeUtils.outputVal(attrName + " : ", buf, fieldPrefix);
i.dataType().output(aVal, buf, "");
TypeUtils.outputVal("\n", buf, "");
}
......@@ -118,9 +121,11 @@ public class StructInstance implements IStruct {
TypeUtils.outputVal("{", buf, prefix);
TypeUtils.outputVal("\n", buf, "");
String fieldPrefix = prefix + "\t";
for(AttributeInfo i : fieldMapping.fields.values()) {
Object aVal = get(i.name);
TypeUtils.outputVal(i.name + " : ", buf, fieldPrefix);
for(Map.Entry<String,AttributeInfo> e : fieldMapping.fields.entrySet()) {
String attrName = e.getKey();
AttributeInfo i = e.getValue();
Object aVal = get(attrName);
TypeUtils.outputVal(attrName + " : ", buf, fieldPrefix);
i.dataType().output(aVal, buf, "");
TypeUtils.outputVal("\n", buf, "");
}
......
/**
* 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.
*/
package org.apache.metadata.types;
import com.google.common.collect.ImmutableMap;
import org.apache.metadata.MetadataException;
import org.apache.metadata.storage.DownCastStructInstance;
import org.apache.metadata.storage.StructInstance;
public class DownCastFieldMapping {
public final ImmutableMap<String, String> fieldNameMap;
protected DownCastFieldMapping(ImmutableMap<String, String> fieldNameMap) {
this.fieldNameMap = fieldNameMap;
}
public void set(DownCastStructInstance s, String attrName, Object val) throws MetadataException {
String mappedNm = fieldNameMap.get(attrName);
if ( mappedNm == null ) {
throw new ValueConversionException(s.getTypeName(), val, "Unknown field " + attrName);
}
s.backingInstance.set(mappedNm, val);
}
public Object get(DownCastStructInstance s, String attrName) throws MetadataException {
String mappedNm = fieldNameMap.get(attrName);
if ( mappedNm == null ) {
throw new ValueConversionException(
String.format("Unknown field %s for Struct %s", attrName, s.getTypeName()));
}
return s.backingInstance.get(mappedNm);
}
}
/**
* 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.
*/
package org.apache.metadata.types;
/**
* @nopublic
*/
public interface ITypeBrowser {
IDataType dataType(String name);
}
......@@ -30,20 +30,28 @@ import java.util.*;
public class StructType extends AbstractDataType<IStruct> {
public final ITypeBrowser typeSystem;
public final String name;
public final FieldMapping fieldMapping;
public final int numFields;
/**
* Used when creating a StructType, to support recursive Structs.
*/
StructType(String name) {
protected StructType(ITypeBrowser typeSystem, String name, int numFields) {
this.typeSystem = typeSystem;
this.name = name;
this.fieldMapping = null;
this.numFields = numFields;
}
StructType(String name, AttributeInfo... fields) throws MetadataException {
protected StructType(ITypeBrowser typeSystem, String name,
ImmutableList<String> superTypes, AttributeInfo... fields) throws MetadataException {
this.typeSystem = typeSystem;
this.name = name;
this.fieldMapping = constructFieldMapping(fields);
this.fieldMapping = constructFieldMapping(superTypes,
fields);
this.numFields = this.fieldMapping.fields.size();
}
@Override
......@@ -51,7 +59,8 @@ public class StructType extends AbstractDataType<IStruct> {
return name;
}
protected FieldMapping constructFieldMapping(AttributeInfo... fields)
protected FieldMapping constructFieldMapping(ImmutableList<String> superTypes,
AttributeInfo... fields)
throws MetadataException {
Map<String,AttributeInfo> fieldsMap = new LinkedHashMap<String, AttributeInfo>();
......@@ -155,10 +164,12 @@ public class StructType extends AbstractDataType<IStruct> {
throw new ValueConversionException(this, val);
}
StructInstance ts = createInstance();
for(AttributeInfo i : fieldMapping.fields.values()) {
Object aVal = s.get(i.name);
for(Map.Entry<String,AttributeInfo> e : fieldMapping.fields.entrySet() ) {
String attrKey = e.getKey();
AttributeInfo i = e.getValue();
Object aVal = s.get(attrKey);
try {
ts.set(i.name, aVal);
ts.set(attrKey, aVal);
} catch(ValueConversionException ve) {
throw new ValueConversionException(this, val, ve);
}
......
/**
* 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.
*/
package org.apache.metadata.types;
import com.google.common.collect.ImmutableList;
public class TraitTypeDefinition {
public final String typeName;
public final ImmutableList<String> superTraits;
public final AttributeDefinition[] attributeDefinitions;
public TraitTypeDefinition(String typeName, ImmutableList<String> superTraits,
AttributeDefinition[] attributeDefinitions) {
this.typeName = typeName;
this.superTraits = superTraits == null ? ImmutableList.<String>of() : superTraits;
this.attributeDefinitions = attributeDefinitions;
}
}
......@@ -15,15 +15,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.metadata.types;
import com.google.common.collect.ImmutableList;
import org.apache.metadata.MetadataException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class TypeSystem {
public class TypeSystem implements ITypeBrowser {
private Map<String, IDataType> types;
......@@ -50,6 +50,10 @@ public class TypeSystem {
types.put(DataTypes.STRING_TYPE.getName(), DataTypes.STRING_TYPE);
}
public IDataType dataType(String name) {
return types.get(name);
}
public IDataType getDataType(String name) throws MetadataException {
if ( types.containsKey(name) ) {
return types.get(name);
......@@ -67,7 +71,7 @@ public class TypeSystem {
AttributeInfo[] infos = new AttributeInfo[attrDefs.length];
Map<Integer, AttributeDefinition> recursiveRefs = new HashMap<Integer, AttributeDefinition>();
try {
types.put(name, new StructType(name));
types.put(name, new StructType(this, name, attrDefs.length));
for (int i = 0; i < attrDefs.length; i++) {
infos[i] = new AttributeInfo(this, attrDefs[i]);
if ( attrDefs[i].dataTypeName == name ) {
......@@ -81,7 +85,7 @@ public class TypeSystem {
types.remove(name);
throw re;
}
StructType sT = new StructType(name, infos);
StructType sT = new StructType(this, name, null, infos);
types.put(name, sT);
for(Map.Entry<Integer, AttributeDefinition> e : recursiveRefs.entrySet()) {
infos[e.getKey()].setDataType(sT);
......@@ -89,6 +93,109 @@ public class TypeSystem {
return sT;
}
public TraitType defineTraitType(boolean errorIfExists,
TraitTypeDefinition traitDef
) throws MetadataException {
Map<String, TraitType> m = defineTraitTypes(errorIfExists, traitDef);
return m.values().iterator().next();
}
public Map<String, TraitType> defineTraitTypes(boolean errorIfExists,
TraitTypeDefinition... traitDefs
) throws MetadataException {
TransientTypeSystem transientTypes = new TransientTypeSystem();
Map<String,TraitTypeDefinition> traitDefMap = new HashMap<String, TraitTypeDefinition>();
/*
* Step 1:
* - validate cannot redefine types
* - setup an empty TraitType to allow for recursive type graphs.
*/
for(TraitTypeDefinition traitDef : traitDefs) {
assert traitDef.typeName != null;
if ( types.containsKey(traitDef.typeName) ) {
throw new MetadataException(String.format("Cannot redefine type %s", traitDef.typeName));
}
transientTypes.traitTypes.put(traitDef.typeName,
new TraitType(transientTypes, traitDef.typeName, traitDef.superTraits,
traitDef.attributeDefinitions.length));
traitDefMap.put(traitDef.typeName, traitDef);
}
/*
* Step 2:
* - validate SuperTypes.
*/
for(TraitTypeDefinition traitDef : traitDefs) {
Set<String> s = new HashSet<String>();
for(String superTraitName : traitDef.superTraits ) {
if (s.contains(superTraitName) ) {
throw new MetadataException(String.format("Trait %s extends superTrait %s multiple times",
traitDef.typeName, superTraitName));
}
IDataType dT = types.get(superTraitName);
dT = dT == null ? transientTypes.traitTypes.get(superTraitName) : dT;
if ( dT == null ) {
throw new MetadataException(String.format("Unknown superType %s in definition of type %s",
superTraitName, traitDef.typeName));
}
if ( dT.getTypeCategory() != DataTypes.TypeCategory.TRAIT ) {
throw new MetadataException(String.format("SuperType %s must be a Trait, in definition of type %s",
superTraitName, traitDef.typeName));
}
s.add(superTraitName);
}
}
/*
* Step 3:
* - Construct TraitTypes in order of SuperType before SubType.
*/
List<TraitType> l = new ArrayList<TraitType>(transientTypes.traitTypes.values());
Collections.sort(l);
List<AttributeInfo> recursiveRefs = new ArrayList<AttributeInfo>();
try {
for (TraitType ttO : l) {
TraitTypeDefinition traitDef = traitDefMap.get(ttO.getName());
AttributeInfo[] infos = new AttributeInfo[traitDef.attributeDefinitions.length];
for (int i = 0; i < traitDef.attributeDefinitions.length; i++) {
infos[i] = new AttributeInfo(this, traitDef.attributeDefinitions[i]);
if (transientTypes.traitTypes.containsKey(traitDef.attributeDefinitions[i].dataTypeName)) {
recursiveRefs.add(infos[i]);
}
}
TraitType tt = new TraitType(this, traitDef.typeName, traitDef.superTraits, infos);
;
types.put(tt.getName(), tt);
}
/*
* Step 4:
* - fix up references in recursive AttrInfo
*/
for (AttributeInfo info : recursiveRefs) {
info.setDataType(dataType(info.dataType().getName()));
}
} catch(MetadataException me) {
for(String sT : transientTypes.traitTypes.keySet()) {
types.remove(sT);
}
throw me;
}
return transientTypes.traitTypes;
}
public DataTypes.ArrayType defineArrayType(IDataType elemType) throws MetadataException {
assert elemType != null;
DataTypes.ArrayType dT = new DataTypes.ArrayType(elemType);
......@@ -103,4 +210,14 @@ public class TypeSystem {
types.put(dT.getName(), dT);
return dT;
}
class TransientTypeSystem implements ITypeBrowser {
Map<String, TraitType> traitTypes = new HashMap<String, TraitType>();
public IDataType dataType(String name) {
IDataType dT = TypeSystem.this.dataType(name);
dT = dT == null ? traitTypes.get(name) : dT;
return dT;
}
}
}
......@@ -18,6 +18,7 @@
package org.apache.metadata;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import junit.framework.TestCase;
......@@ -40,11 +41,13 @@ public abstract class BaseTest {
public static final String STRUCT_TYPE_1 = "t1";
public static final String STRUCT_TYPE_2 = "t2";
@BeforeClass
public static void setupClass() throws MetadataException {
@Before
public void setup() throws MetadataException {
TypeSystem ts = new TypeSystem();
MemRepository mr = new MemRepository();
MetadataService.setCurrentService(new MetadataService(mr, ts));
ms = new MetadataService(mr, ts);
MetadataService.setCurrentService(ms);
StructType structType = ts.defineStructType(STRUCT_TYPE_1,
true,
......@@ -95,11 +98,6 @@ public abstract class BaseTest {
return s;
}
@Before
public void setup() throws MetadataException {
ms = MetadataService.getCurrentService();
}
public static AttributeDefinition createOptionalAttrDef(String name,
IDataType dataType
) {
......@@ -128,4 +126,15 @@ public abstract class BaseTest {
return new AttributeDefinition(name, dataType, Multiplicity.REQUIRED, false, null);
}
protected Map<String, TraitType> defineTraits(TraitTypeDefinition... tDefs) throws MetadataException {
return ms.getTypeSystem().defineTraitTypes(true, tDefs);
}
protected TraitTypeDefinition createTraitTypeDef(String name, ImmutableList<String> superTypes,
AttributeDefinition... attrDefs) {
return new TraitTypeDefinition(name, superTypes, attrDefs);
}
}
package org.apache.metadata;
import com.google.common.collect.ImmutableList;
import org.apache.metadata.storage.StructInstance;
import org.apache.metadata.types.*;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
public class TraitTest extends BaseTest {
@Before
public void setup() throws MetadataException {
super.setup();
}
/*
* Type Hierarchy is:
* A(a,b,c,d)
* B(b) extends A
* C(c) extends A
* D(d) extends B,C
*
* - There are a total of 11 fields in an instance of D
* - an attribute that is hidden by a SubType can referenced by prefixing it with the complete Path.
* For e.g. the 'b' attribute in A (that is a superType for B) is hidden the 'b' attribute in B.
* So it is availabel by the name 'A.B.D.b'
*
* - Another way to set attributes is to cast. Casting a 'D' instance of 'B' makes the 'A.B.D.b' attribute
* available as 'A.B.b'. Casting one more time to an 'A' makes the 'A.B.b' attribute available as 'b'.
*/
@Test
public void test1() throws MetadataException {
TraitTypeDefinition A = createTraitTypeDef("A", null,
createRequiredAttrDef("a", DataTypes.INT_TYPE),
createOptionalAttrDef("b", DataTypes.BOOLEAN_TYPE),
createOptionalAttrDef("c", DataTypes.BYTE_TYPE),
createOptionalAttrDef("d", DataTypes.SHORT_TYPE));
TraitTypeDefinition B = createTraitTypeDef("B", ImmutableList.<String>of("A"),
createOptionalAttrDef("b", DataTypes.BOOLEAN_TYPE));
TraitTypeDefinition C = createTraitTypeDef("C", ImmutableList.<String>of("A"),
createOptionalAttrDef("c", DataTypes.BYTE_TYPE));
TraitTypeDefinition D = createTraitTypeDef("D", ImmutableList.<String>of("B", "C"),
createOptionalAttrDef("d", DataTypes.SHORT_TYPE));
defineTraits(A, B, C, D);
TraitType DType = (TraitType) ms.getTypeSystem().getDataType("D");
Struct s1 = new Struct("D");
s1.set("d", 1);
s1.set("c", 1);
s1.set("b", true);
s1.set("a", 1);
s1.set("A.B.D.b", true);
s1.set("A.B.D.c", 2);
s1.set("A.B.D.d", 2);
s1.set("A.C.D.a", 3);
s1.set("A.C.D.b", false);
s1.set("A.C.D.c", 3);
s1.set("A.C.D.d", 3);
StructInstance ts = DType.convert(s1, Multiplicity.REQUIRED);
System.out.println(ts);
/*
* cast to B and set the 'b' attribute on A.
*/
TraitType BType = (TraitType) ms.getTypeSystem().getDataType("B");
IStruct s2 = DType.castAs(ts, "B");
s2.set("A.B.b", false);
System.out.println(ts);
/*
* cast again to A and set the 'b' attribute on A.
*/
TraitType AType = (TraitType) ms.getTypeSystem().getDataType("A");
IStruct s3 = BType.castAs(s2, "A");
s3.set("b", true);
System.out.println(ts);
}
}
......@@ -18,13 +18,12 @@
package org.apache.metadata.json
import com.google.common.collect.ImmutableList
import org.apache.metadata.Struct
import org.apache.metadata.storage.StructInstance
import org.apache.metadata.storage.StructInstance
import org.apache.metadata.types.Multiplicity
import org.apache.metadata.types.StructType
import org.apache.metadata.types._
import org.apache.metadata.{Struct, BaseTest}
import org.apache.metadata.types.{Multiplicity, StructType}
import org.json4s.NoTypeHints
import org.junit.Before
import org.junit.Test
......@@ -78,4 +77,52 @@ class SerializationTest extends BaseTest {
println("Typed Struct read from string:")
println(ts1)
}
@Test def testTrait {
val A: TraitTypeDefinition = createTraitTypeDef("A", null,
BaseTest.createRequiredAttrDef("a", DataTypes.INT_TYPE),
BaseTest.createOptionalAttrDef("b", DataTypes.BOOLEAN_TYPE),
BaseTest.createOptionalAttrDef("c", DataTypes.BYTE_TYPE),
BaseTest.createOptionalAttrDef("d", DataTypes.SHORT_TYPE))
val B: TraitTypeDefinition = createTraitTypeDef("B", ImmutableList.of[String]("A"),
BaseTest.createOptionalAttrDef("b", DataTypes.BOOLEAN_TYPE))
val C: TraitTypeDefinition = createTraitTypeDef("C", ImmutableList.of[String]("A"),
BaseTest.createOptionalAttrDef("c", DataTypes.BYTE_TYPE))
val D: TraitTypeDefinition = createTraitTypeDef("D", ImmutableList.of[String]("B", "C"),
BaseTest.createOptionalAttrDef("d", DataTypes.SHORT_TYPE))
defineTraits(A, B, C, D)
val DType: TraitType = ms.getTypeSystem.getDataType("D").asInstanceOf[TraitType]
val s1: Struct = new Struct("D")
s1.set("d", 1)
s1.set("c", 1)
s1.set("b", true)
s1.set("a", 1)
s1.set("A.B.D.b", true)
s1.set("A.B.D.c", 2)
s1.set("A.B.D.d", 2)
s1.set("A.C.D.a", 3)
s1.set("A.C.D.b", false)
s1.set("A.C.D.c", 3)
s1.set("A.C.D.d", 3)
val s: Struct = BaseTest.createStruct(ms)
val ts: StructInstance = DType.convert(s1, Multiplicity.REQUIRED)
implicit val formats = org.json4s.native.Serialization.formats(NoTypeHints) + new TypedStructSerializer +
new BigDecimalSerializer + new BigIntegerSerializer
println("Typed Struct :")
println(ts)
val ser = swrite(ts)
println("Json representation :")
println(ser)
val ts1 = read[StructInstance](
"""
{"$typeName$":"D","A.C.D.d":3,"A.B.D.c":2,"b":true,"A.C.D.c":3,"d":1,
"A.B.D.b":true,"a":1,"A.C.D.b":false,"A.B.D.d":2,"c":1,"A.C.D.a":3}""")
println("Typed Struct read from string:")
println(ts1)
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment