/** * 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.type; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.TypeCategory; import org.apache.atlas.model.typedef.AtlasBaseTypeDef; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import static org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef.COUNT_NOT_SET; /** * class that implements behaviour of an array-type. */ public class AtlasArrayType extends AtlasType { private static final Logger LOG = LoggerFactory.getLogger(AtlasArrayType.class); private final String elementTypeName; private int minCount; private int maxCount; private AtlasType elementType; public AtlasArrayType(AtlasType elementType) { this(elementType, COUNT_NOT_SET, COUNT_NOT_SET); } public AtlasArrayType(AtlasType elementType, int minCount, int maxCount) { super(AtlasBaseTypeDef.getArrayTypeName(elementType.getTypeName()), TypeCategory.ARRAY); this.elementTypeName = elementType.getTypeName(); this.minCount = minCount; this.maxCount = maxCount; this.elementType = elementType; } public AtlasArrayType(String elementTypeName) { this(elementTypeName, COUNT_NOT_SET, COUNT_NOT_SET); } public AtlasArrayType(String elementTypeName, int minCount, int maxCount) { super(AtlasBaseTypeDef.getArrayTypeName(elementTypeName), TypeCategory.ARRAY); this.elementTypeName = elementTypeName; this.minCount = minCount; this.maxCount = maxCount; this.elementType = null; } public AtlasArrayType(String elementTypeName, AtlasTypeRegistry typeRegistry) throws AtlasBaseException { this(elementTypeName, COUNT_NOT_SET, COUNT_NOT_SET, typeRegistry); } public AtlasArrayType(String elementTypeName, int minCount, int maxCount, AtlasTypeRegistry typeRegistry) throws AtlasBaseException { super(AtlasBaseTypeDef.getArrayTypeName(elementTypeName), TypeCategory.ARRAY); this.elementTypeName = elementTypeName; this.minCount = minCount; this.maxCount = maxCount; this.resolveReferences(typeRegistry); } public String getElementTypeName() { return elementTypeName; } public void setMinCount(int minCount) { this.minCount = minCount; } public int getMinCount() { return minCount; } public void setMaxCount(int maxCount) { this.maxCount = maxCount; } public int getMaxCount() { return maxCount; } public AtlasType getElementType() { return elementType; } @Override public void resolveReferences(AtlasTypeRegistry typeRegistry) throws AtlasBaseException { elementType = typeRegistry.getType(elementTypeName); } @Override public Collection<?> createDefaultValue() { Collection<Object> ret = new ArrayList<>(); ret.add(elementType.createDefaultValue()); if (minCount != COUNT_NOT_SET) { for (int i = 1; i < minCount; i++) { ret.add(elementType.createDefaultValue()); } } return ret; } @Override public boolean isValidValue(Object obj) { if (obj != null) { if (obj instanceof List || obj instanceof Set) { Collection objList = (Collection) obj; if (!isValidElementCount(objList.size())) { return false; } for (Object element : objList) { if (!elementType.isValidValue(element)) { return false; } } } else if (obj.getClass().isArray()) { int arrayLen = Array.getLength(obj); if (!isValidElementCount(arrayLen)) { return false; } for (int i = 0; i < arrayLen; i++) { if (!elementType.isValidValue(Array.get(obj, i))) { return false; } } } else { return false; // invalid type } } return true; } @Override public boolean isValidValueForUpdate(Object obj) { if (obj != null) { if (obj instanceof List || obj instanceof Set) { Collection objList = (Collection) obj; if (!isValidElementCount(objList.size())) { return false; } for (Object element : objList) { if (!elementType.isValidValueForUpdate(element)) { return false; } } } else if (obj.getClass().isArray()) { int arrayLen = Array.getLength(obj); if (!isValidElementCount(arrayLen)) { return false; } for (int i = 0; i < arrayLen; i++) { if (!elementType.isValidValueForUpdate(Array.get(obj, i))) { return false; } } } else { return false; // invalid type } } return true; } @Override public Collection<?> getNormalizedValue(Object obj) { if (obj == null) { return null; } if (obj instanceof List || obj instanceof Set) { List<Object> ret = new ArrayList<>(); Collection objList = (Collection) obj; if (!isValidElementCount(objList.size())) { return null; } for (Object element : objList) { if (element != null) { Object normalizedValue = elementType.getNormalizedValue(element); if (normalizedValue != null) { ret.add(normalizedValue); } else { return null; // invalid element value } } else { ret.add(element); } } return ret; } else if (obj.getClass().isArray()) { List<Object> ret = new ArrayList<>(); int arrayLen = Array.getLength(obj); if (!isValidElementCount(arrayLen)) { return null; } for (int i = 0; i < arrayLen; i++) { Object element = Array.get(obj, i); if (element != null) { Object normalizedValue = elementType.getNormalizedValue(element); if (normalizedValue != null) { ret.add(normalizedValue); } else { return null; // invalid element value } } else { ret.add(element); } } return ret; } return null; } @Override public Collection<?> getNormalizedValueForUpdate(Object obj) { if (obj == null) { return null; } if (obj instanceof List || obj instanceof Set) { List<Object> ret = new ArrayList<>(); Collection objList = (Collection) obj; if (!isValidElementCount(objList.size())) { return null; } for (Object element : objList) { if (element != null) { Object normalizedValue = elementType.getNormalizedValueForUpdate(element); if (normalizedValue != null) { ret.add(normalizedValue); } else { return null; // invalid element value } } else { ret.add(element); } } return ret; } else if (obj.getClass().isArray()) { List<Object> ret = new ArrayList<>(); int arrayLen = Array.getLength(obj); if (!isValidElementCount(arrayLen)) { return null; } for (int i = 0; i < arrayLen; i++) { Object element = Array.get(obj, i); if (element != null) { Object normalizedValue = elementType.getNormalizedValueForUpdate(element); if (normalizedValue != null) { ret.add(normalizedValue); } else { return null; // invalid element value } } else { ret.add(element); } } return ret; } return null; } @Override public boolean validateValue(Object obj, String objName, List<String> messages) { boolean ret = true; if (obj != null) { if (obj instanceof List || obj instanceof Set) { Collection objList = (Collection) obj; if (!isValidElementCount(objList.size())) { ret = false; messages.add(objName + ": incorrect number of values. found=" + objList.size() + "; expected: minCount=" + minCount + ", maxCount=" + maxCount); } int idx = 0; for (Object element : objList) { ret = elementType.validateValue(element, objName + "[" + idx + "]", messages) && ret; idx++; } } else if (obj.getClass().isArray()) { int arrayLen = Array.getLength(obj); if (!isValidElementCount(arrayLen)) { ret = false; messages.add(objName + ": incorrect number of values. found=" + arrayLen + "; expected: minCount=" + minCount + ", maxCount=" + maxCount); } for (int i = 0; i < arrayLen; i++) { ret = elementType.validateValue(Array.get(obj, i), objName + "[" + i + "]", messages) && ret; } } else { ret = false; messages.add(objName + "=" + obj + ": invalid value for type " + getTypeName()); } } return ret; } @Override public boolean validateValueForUpdate(Object obj, String objName, List<String> messages) { boolean ret = true; if (obj != null) { if (obj instanceof List || obj instanceof Set) { Collection objList = (Collection) obj; if (!isValidElementCount(objList.size())) { ret = false; messages.add(objName + ": incorrect number of values. found=" + objList.size() + "; expected: minCount=" + minCount + ", maxCount=" + maxCount); } int idx = 0; for (Object element : objList) { ret = elementType.validateValueForUpdate(element, objName + "[" + idx + "]", messages) && ret; idx++; } } else if (obj.getClass().isArray()) { int arrayLen = Array.getLength(obj); if (!isValidElementCount(arrayLen)) { ret = false; messages.add(objName + ": incorrect number of values. found=" + arrayLen + "; expected: minCount=" + minCount + ", maxCount=" + maxCount); } for (int i = 0; i < arrayLen; i++) { ret = elementType.validateValueForUpdate(Array.get(obj, i), objName + "[" + i + "]", messages) && ret; } } else { ret = false; messages.add(objName + "=" + obj + ": invalid value for type " + getTypeName()); } } return ret; } @Override public AtlasType getTypeForAttribute() { AtlasType elementAttributeType = elementType.getTypeForAttribute(); if (elementAttributeType == elementType) { return this; } else { AtlasType attributeType = new AtlasArrayType(elementAttributeType, minCount, maxCount); if (LOG.isDebugEnabled()) { LOG.debug("getTypeForAttribute(): {} ==> {}", getTypeName(), attributeType.getTypeName()); } return attributeType; } } private boolean isValidElementCount(int count) { if (minCount != COUNT_NOT_SET) { if (count < minCount) { return false; } } if (maxCount != COUNT_NOT_SET) { if (count > maxCount) { return false; } } return true; } }