blob: 42d9c9fda22e1cdebf25972222542f837eb5df43 [file] [log] [blame]
/**
* 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.atlas.typesystem.types;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.atlas.AtlasException;
import org.apache.atlas.typesystem.IStruct;
import org.apache.atlas.typesystem.ITypedStruct;
public class StructType extends AbstractDataType<IStruct> implements IConstructableType<IStruct, ITypedStruct> {
public TypeSystem typeSystem;
public final FieldMapping fieldMapping;
public final Map<AttributeInfo, List<String>> infoToNameMap;
public final int numFields;
private final TypedStructHandler handler;
protected StructType(TypeSystem typeSystem, String name, String description, int numFields) {
super(name, description);
this.typeSystem = typeSystem;
this.fieldMapping = null;
infoToNameMap = null;
this.numFields = numFields;
this.handler = null;
}
protected StructType(TypeSystem typeSystem, String name, String description, AttributeInfo... fields)
throws AtlasException {
super(name, description);
this.typeSystem = typeSystem;
this.fieldMapping = constructFieldMapping(fields);
infoToNameMap = TypeUtils.buildAttrInfoToNameMap(this.fieldMapping);
this.numFields = this.fieldMapping.fields.size();
this.handler = new TypedStructHandler(this);
}
public void setTypeSystem(TypeSystem typeSystem) {
this.typeSystem = typeSystem;
}
public FieldMapping fieldMapping() {
return fieldMapping;
}
/**
* Validate that current definition can be updated with the new definition
* @param newType
* @return true if the current definition can be updated with the new definition, else false
*/
@Override
public void validateUpdate(IDataType newType) throws TypeUpdateException {
super.validateUpdate(newType);
StructType newStructType = (StructType) newType;
try {
TypeUtils.validateUpdate(fieldMapping, newStructType.fieldMapping);
} catch (TypeUpdateException e) {
throw new TypeUpdateException(newType, e);
}
}
protected FieldMapping constructFieldMapping(AttributeInfo... fields)
throws AtlasException {
Map<String, AttributeInfo> fieldsMap = new LinkedHashMap<String, AttributeInfo>();
Map<String, Integer> fieldPos = new HashMap<String, Integer>();
Map<String, Integer> fieldNullPos = new HashMap<String, Integer>();
int numBools = 0;
int numBytes = 0;
int numShorts = 0;
int numInts = 0;
int numLongs = 0;
int numFloats = 0;
int numDoubles = 0;
int numBigInts = 0;
int numBigDecimals = 0;
int numDates = 0;
int numStrings = 0;
int numArrays = 0;
int numMaps = 0;
int numStructs = 0;
int numReferenceables = 0;
for (AttributeInfo i : fields) {
if (fieldsMap.containsKey(i.name)) {
throw new AtlasException(
String.format("Struct defintion cannot contain multiple fields with the same " + "name %s",
i.name));
}
fieldsMap.put(i.name, i);
fieldNullPos.put(i.name, fieldNullPos.size());
if (i.dataType() == DataTypes.BOOLEAN_TYPE) {
fieldPos.put(i.name, numBools);
numBools++;
} else if (i.dataType() == DataTypes.BYTE_TYPE) {
fieldPos.put(i.name, numBytes);
numBytes++;
} else if (i.dataType() == DataTypes.SHORT_TYPE) {
fieldPos.put(i.name, numShorts);
numShorts++;
} else if (i.dataType() == DataTypes.INT_TYPE) {
fieldPos.put(i.name, numInts);
numInts++;
} else if (i.dataType() == DataTypes.LONG_TYPE) {
fieldPos.put(i.name, numLongs);
numLongs++;
} else if (i.dataType() == DataTypes.FLOAT_TYPE) {
fieldPos.put(i.name, numFloats);
numFloats++;
} else if (i.dataType() == DataTypes.DOUBLE_TYPE) {
fieldPos.put(i.name, numDoubles);
numDoubles++;
} else if (i.dataType() == DataTypes.BIGINTEGER_TYPE) {
fieldPos.put(i.name, numBigInts);
numBigInts++;
} else if (i.dataType() == DataTypes.BIGDECIMAL_TYPE) {
fieldPos.put(i.name, numBigDecimals);
numBigDecimals++;
} else if (i.dataType() == DataTypes.DATE_TYPE) {
fieldPos.put(i.name, numDates);
numDates++;
} else if (i.dataType() == DataTypes.STRING_TYPE) {
fieldPos.put(i.name, numStrings);
numStrings++;
} else if (i.dataType().getTypeCategory() == DataTypes.TypeCategory.ENUM) {
fieldPos.put(i.name, numInts);
numInts++;
} else if (i.dataType().getTypeCategory() == DataTypes.TypeCategory.ARRAY) {
fieldPos.put(i.name, numArrays);
numArrays++;
} else if (i.dataType().getTypeCategory() == DataTypes.TypeCategory.MAP) {
fieldPos.put(i.name, numMaps);
numMaps++;
} else if (i.dataType().getTypeCategory() == DataTypes.TypeCategory.STRUCT
|| i.dataType().getTypeCategory() == DataTypes.TypeCategory.TRAIT) {
fieldPos.put(i.name, numStructs);
numStructs++;
} else if (i.dataType().getTypeCategory() == DataTypes.TypeCategory.CLASS) {
fieldPos.put(i.name, numReferenceables);
numReferenceables++;
} else {
throw new AtlasException(String.format("Unknown datatype %s", i.dataType()));
}
}
return new FieldMapping(fieldsMap, fieldPos, fieldNullPos, numBools, numBytes, numShorts, numInts, numLongs,
numFloats, numDoubles, numBigInts, numBigDecimals, numDates, numStrings, numArrays, numMaps, numStructs,
numReferenceables);
}
@Override
public DataTypes.TypeCategory getTypeCategory() {
return DataTypes.TypeCategory.STRUCT;
}
@Override
public ITypedStruct convert(Object val, Multiplicity m) throws AtlasException {
return handler.convert(val, m);
}
public ITypedStruct createInstance() {
return handler.createInstance();
}
@Override
public void output(IStruct s, Appendable buf, String prefix, Set<IStruct> inProcess) throws AtlasException {
handler.output(s, buf, prefix, inProcess);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
try {
output(buf, new HashSet<String>());
}
catch (AtlasException e) {
throw new RuntimeException(e);
}
return buf.toString();
}
@Override
public void output(Appendable buf, Set<String> typesInProcess) throws AtlasException {
if (typesInProcess == null) {
typesInProcess = new HashSet<>();
}
else if (typesInProcess.contains(name)) {
// Avoid infinite recursion on bi-directional reference attributes.
try {
buf.append(name);
} catch (IOException e) {
throw new AtlasException(e);
}
return;
}
typesInProcess.add(name);
try {
buf.append(getClass().getSimpleName());
buf.append("{name=").append(name);
buf.append(", description=").append(description);
buf.append(", fieldMapping.fields=[");
Iterator<AttributeInfo> it = fieldMapping.fields.values().iterator();
while (it.hasNext()) {
AttributeInfo attrInfo = it.next();
attrInfo.output(buf, typesInProcess);
if (it.hasNext()) {
buf.append(", ");
}
else {
buf.append(']');
}
}
buf.append("}");
}
catch(IOException e) {
throw new AtlasException(e);
}
finally {
typesInProcess.remove(name);
}
}
@Override
public void updateSignatureHash(MessageDigest digester, Object val) throws AtlasException {
if( !(val instanceof ITypedStruct)) {
throw new IllegalArgumentException("Unexpected value type " + val.getClass().getSimpleName() + ". Expected instance of ITypedStruct");
}
digester.update(getName().getBytes(Charset.forName("UTF-8")));
if(fieldMapping.fields != null && val != null) {
IStruct typedValue = (IStruct) val;
for (AttributeInfo aInfo : fieldMapping.fields.values()) {
Object attrVal = typedValue.get(aInfo.name);
if(attrVal != null) {
aInfo.dataType().updateSignatureHash(digester, attrVal);
}
}
}
}
public List<String> getNames(AttributeInfo info) {
return infoToNameMap.get(info);
}
}