/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.catalog.data.impl;
import static org.apache.commons.lang.Validate.notEmpty;
import static org.apache.commons.lang.Validate.notNull;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import ddf.catalog.data.AttributeDescriptor;
import ddf.catalog.data.MetacardType;
import ddf.catalog.data.impl.types.CoreAttributes;
import ddf.catalog.data.impl.types.SecurityAttributes;
/**
* Default implementation of the {@link MetacardType}, used by {@link BasicTypes} to create the
* {@link BasicTypes#BASIC_METACARD}.
* <p>
* This class is {@link java.io.Serializable} and care should be taken with compatibility if changes are
* made.
* </p>
* <p>
* For what constitutes a compatible change in serialization, see <a href=
* "http://docs.oracle.com/javase/6/docs/platform/serialization/spec/version.html#6678" >Sun's
* Guidelines</a>.
* </p>
*/
public class MetacardTypeImpl implements MetacardType {
private static final long serialVersionUID = 1L;
/**
* Set of {@link AttributeDescriptor}s
*/
protected transient Set<AttributeDescriptor> descriptors = new HashSet<>();
/**
* The name of this {@code MetacardTypeImpl}
*
* @serial
*/
protected String name;
/**
* Creates a {@code MetacardTypeImpl} with the provided {@code name} and
* {@link AttributeDescriptor}s.
*
* @param name the name of this {@code MetacardTypeImpl}
* @param descriptors the set of descriptors for this {@code MetacardTypeImpl}
*/
public MetacardTypeImpl(String name, Set<AttributeDescriptor> descriptors) {
/*
* If any defensive logic is added to this constructor, then that logic should be reflected
* in the deserialization (readObject()) of this object so that the integrity of a
* serialized object is maintained. For instance, if a null check is added in the
* constructor, the same check should be added in the readObject() method.
*/
this.name = name;
if (descriptors != null) {
this.descriptors.addAll(descriptors);
}
}
/**
* Creates a {@code MetacardTypeImpl} with the provided name, {@link MetacardType}, and set of
* additional {@linkplain AttributeDescriptor AttributeDescriptors}.
* <p>
* {@code additionalDescriptors} and the descriptors in {@code metacardType} will be combined to
* form the set of descriptors for this {@code MetacardTypeImpl}.
* <p>
* Essentially, this is a convenience constructor for creating a new {@code MetacardTypeImpl}
* that extends an existing {@link MetacardType}.
*
* @param name the name of this {@code MetacardTypeImpl}
* @param metacardType the base {@link MetacardType}, cannot be null
* @param additionalDescriptors the descriptors to add to this {@code MetacardTypeImpl} in
* addition to the descriptors in {@code metacardType}, cannot be
* null or empty
* @throws IllegalArgumentException if {@code metacardType} or {@code additionalDescriptors} is
* null, or if {@code additionalDescriptors} is empty
*/
public MetacardTypeImpl(String name, MetacardType metacardType,
Set<AttributeDescriptor> additionalDescriptors) {
notNull(metacardType, "The metacard type cannot be null.");
notEmpty(additionalDescriptors,
"The set of additional descriptors cannot be null or empty");
this.name = name;
descriptors.addAll(metacardType.getAttributeDescriptors());
descriptors.addAll(additionalDescriptors);
}
/**
* Creates a {@code MetacardTypeImpl} with the provided {@code name} and
* {@link MetacardType}s.
*
* @param name the name of this {@code MetacardTypeImpl}
* @param metacardTypes the set of {@link MetacardType}s to compose this {@code MetacardTypeImpl}
*/
public MetacardTypeImpl(String name, List<MetacardType> metacardTypes) {
this.name = name;
List<String> attributeNames = new ArrayList<>();
addAttributeDescriptors(attributeNames, new CoreAttributes().getAttributeDescriptors());
addAttributeDescriptors(attributeNames, new SecurityAttributes().getAttributeDescriptors());
if (metacardTypes != null) {
metacardTypes.forEach(metacardType -> addAttributeDescriptors(attributeNames,
metacardType.getAttributeDescriptors()));
}
}
private List<String> addAttributeDescriptors(List<String> attributeNames,
Set<AttributeDescriptor> attributeDescriptors) {
attributeDescriptors.forEach(attributeDescriptor -> {
if (!attributeNames.contains(attributeDescriptor.getName())) {
descriptors.add(attributeDescriptor);
attributeNames.add(attributeDescriptor.getName());
}
});
return attributeNames;
}
@Override
public String getName() {
return name;
}
@Override
public Set<AttributeDescriptor> getAttributeDescriptors() {
return Collections.unmodifiableSet(descriptors);
}
@Override
public AttributeDescriptor getAttributeDescriptor(String attributeName) {
if (attributeName == null) {
return null;
}
// TODO could this be faster?
for (AttributeDescriptor descriptor : descriptors) {
if (attributeName.equals(descriptor.getName())) {
return descriptor;
}
}
return null;
}
/**
* Serializes this {@link MetacardTypeImpl} instance.
*
* @param stream the {@link ObjectOutputStream} that contains the object to be serialized
* @throws IOException
* @serialData First, the name is written as a {@code String} by the default Java serialization
* implementation, then the number of {@link AttributeDescriptor} objects is written
* as an ( {@code int}), followed by all of the {@code AttributeDescriptor} objects
* in no guaranteed sequence or order.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
/*
* defaultWriteObject() is invoked for greater flexibility and compatibility. See the
* *Serialization Note* in MetacardImpl's class Javadoc.
*/
stream.defaultWriteObject();
stream.writeInt(descriptors.size());
for (AttributeDescriptor descriptor : descriptors) {
stream.writeObject(descriptor);
}
}
/**
* Deserializes this {@link MetacardTypeImpl} instance.
*
* @param stream the {@link ObjectInputStream} that contains the bytes of the object
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
/*
* defaultReadObject() is invoked for greater flexibility and compatibility. See the
* *Serialization Note* in MetacardImpl's class Javadoc.
*/
stream.defaultReadObject();
int numElements = stream.readInt();
descriptors = new HashSet<AttributeDescriptor>();
for (int i = 0; i < numElements; i++) {
descriptors.add((AttributeDescriptor) stream.readObject());
}
}
@Override
public int hashCode() {
return new HashCodeBuilder(19, 71).append(name)
.append(descriptors)
.toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof MetacardType)) {
return false;
}
MetacardType other = (MetacardType) obj;
return new EqualsBuilder().append(name, other.getName())
.append(descriptors, other.getAttributeDescriptors())
.isEquals();
}
}