/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.nuxeo.ecm.core.api.model.impl;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.nuxeo.common.collections.PrimitiveArrays;
import org.nuxeo.ecm.core.api.PropertyException;
import org.nuxeo.ecm.core.api.model.Property;
import org.nuxeo.ecm.core.api.model.PropertyConversionException;
import org.nuxeo.ecm.core.schema.types.Field;
import org.nuxeo.ecm.core.schema.types.JavaTypes;
import org.nuxeo.ecm.core.schema.types.ListType;
import org.nuxeo.ecm.core.schema.types.Type;
import org.nuxeo.ecm.core.schema.types.TypeException;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class ArrayProperty extends ScalarProperty {
private static final long serialVersionUID = 0L;
public ArrayProperty(Property parent, Field field, int flags) {
super(parent, field, flags);
}
@Override
public ListType getType() {
return (ListType) super.getType();
}
@Override
public boolean isContainer() {
return false;
}
@Override
public void setValue(Object value) throws PropertyException {
// this code manage dirty status for the arrayproperty and its childs values
// it checks whether the property changed, or their index changed
if (value == null) {
childDirty = new boolean[0];
super.setValue(null);
} else {
Object[] oldValues = (Object[]) internalGetValue();
boolean[] oldChildDirty = getChildDirty();
super.setValue(value);
Object[] newValues = (Object[]) internalGetValue();
boolean[] newChildDirty = new boolean[newValues != null ? newValues.length : 0];
for (int i = 0; i < newChildDirty.length; i++) {
Object newValue = newValues[i];
if (oldValues == null || i >= oldValues.length) {
newChildDirty[i] = true;
} else {
Object oldValue = oldValues[i];
if (!((newValue == null && oldValue == null) || (newValue != null && newValue.equals(oldValue)))) {
newChildDirty[i] = true;
} else {
newChildDirty[i] = false || oldChildDirty[i];
}
}
}
childDirty = newChildDirty;
}
}
@Override
protected boolean isSameValue(Serializable value1, Serializable value2) {
Object[] castedtValue1 = (Object[]) value1;
Object[] castedtValue2 = (Object[]) value2;
return castedtValue1 == castedtValue2 || (castedtValue1 == null && castedtValue2.length == 0)
|| (castedtValue2 == null && castedtValue1.length == 0) || Arrays.equals(castedtValue1, castedtValue2);
}
@Override
public boolean isNormalized(Object value) {
return value == null || value.getClass().isArray();
}
@Override
public Serializable normalize(Object value) throws PropertyConversionException {
if (value == null) {
return null;
} else if (value.getClass().isArray()) {
return convert(Arrays.asList((Object[]) value));
} else if (value instanceof Collection) {
return convert((Collection<?>) value);
}
throw new PropertyConversionException(value.getClass(), Object[].class, getXPath());
}
protected Serializable convert(Collection<?> value) throws PropertyConversionException {
try {
Type fieldType = getType().getFieldType();
Collection<Object> col = new ArrayList<>(value.size());
for (Object v : value) {
if (v == null) {
col.add(null);
} else {
col.add(fieldType.convert(v));
}
}
Class<?> klass = JavaTypes.getClass(fieldType);
return col.toArray((Object[]) Array.newInstance(klass, col.size()));
} catch (TypeException e) {
throw new PropertyConversionException("Unable to convert collection value to desired type.", e);
}
}
@SuppressWarnings("unchecked")
@Override
public <T> T convertTo(Serializable value, Class<T> toType) throws PropertyConversionException {
if (toType.isArray()) {
return (T) PrimitiveArrays.toObjectArray(value);
} else if (Collection.class.isAssignableFrom(toType)) {
return (T) Arrays.asList((Object[]) value);
}
throw new PropertyConversionException(value.getClass(), toType);
}
@Override
public Object newInstance() {
return new Serializable[0];
}
// this boolean array managed the dirty flags for arrayproperty childs
private boolean[] childDirty = null;
protected boolean[] getChildDirty() {
if (childDirty == null) {
Object[] oldValues = (Object[]) internalGetValue();
if (oldValues == null) {
childDirty = new boolean[0];
} else {
childDirty = new boolean[oldValues.length];
for (int i = 0; i < childDirty.length; i++) {
childDirty[i] = false;
}
}
}
return childDirty;
}
/**
* This method provides a way to know if some arrayproperty values are dirty: value or index changed. since 7.2
*/
public boolean isDirty(int index) {
if (index > getChildDirty().length) {
throw new IndexOutOfBoundsException(
"Index out of bounds: " + index + ". Bounds are: 0 - " + (getChildDirty().length - 1));
}
return getChildDirty()[index];
}
@Override
public void clearDirtyFlags() {
// even makes child properties not dirty
super.clearDirtyFlags();
for (int i = 0; i < getChildDirty().length; i++) {
childDirty[i] = false;
}
}
}