/** * 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 java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.LinkedList; import java.util.List; import ddf.catalog.data.Attribute; /** * A simple implementation of {@link Attribute}. * * <p> * This class is {@link 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> * * @author ddf.isgs@lmco.com * */ public class AttributeImpl implements Attribute { private static final long serialVersionUID = 1L; /** * Nontransient field that holds the name of the {@link Attribute}. * * @serial */ protected String name; private transient List<Serializable> values; /** * Constructor * * @param name * - the name of this {@link Attribute} * @param value * - the value of this {@link Attribute} */ public AttributeImpl(String name, Serializable value) { /* * 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; this.values = createPopulatedList(value); } @Override public String getName() { return name; } @Override public Serializable getValue() { if (!values.isEmpty()) { return values.get(0); } else { return null; } } private List<Serializable> createPopulatedList(Serializable value) { List<Serializable> list = new LinkedList<Serializable>(); list.add(value); return list; } @Override public List<Serializable> getValues() { return values; } /** * Adds a value to this {@link Attribute} * * @param value * the value to add */ public void addValue(Serializable value) { values.add(value); } /** * Clears all {@link Attribute} values */ public void clearValues() { values.clear(); } /** * Serializes this {@link AttributeImpl} instance. * * @serialData First, all non-transient fields are written out by the default Java serialization * implementation (ObjectInputStream.defaultWriteObject()). Then the number of * "value" objects is written out as an ({@code int}). After the number of objects, * each "value" object is written out (each as {@code Serializable}). * @param s * - the {@link ObjectOutputStream} which contains the object to be serialized * @throws IOException */ private void writeObject(ObjectOutputStream s) throws IOException { /* * defaultWriteObject() is invoked for greater flexibility and compatibility. See the * *Serialization Note* in MetacardImpl's class Javadoc. */ s.defaultWriteObject(); s.writeInt(values.size()); for (Serializable ser : values) { s.writeObject(ser); } } /** * Deserializes this {@link AttributeImpl}'s instance. * * @param stream * the {@link ObjectInputStream} that contains the bytes of the object * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { /* * defaultReadObject() is invoked for greater flexibility and compatibility. See the * *Serialization Note* in MetacardImpl's class Javadoc. */ s.defaultReadObject(); int numElements = s.readInt(); validateNonEmpty(numElements); values = new LinkedList<Serializable>(); for (int i = 0; i < numElements; i++) { values.add((Serializable) s.readObject()); } validateUntampered(numElements); } private void validateUntampered(int numElements) throws InvalidObjectException { // Invariant: When the object was serialized, the integer written to // disk matched the number of value objects written to disk. if (values.size() != numElements) { throw new InvalidObjectException( "Corrupt object: written number of values does not match actual number of values."); } } private void validateNonEmpty(int numElements) throws InvalidObjectException { // Invariant: This implementation does not allow an Attribute object // with no values if (numElements == 0) { throw new InvalidObjectException("Cannot construct " + this.getClass().getName() + " object without any values."); } } @Override public String toString() { return getClass().getName() + " {name=" + this.name + ", values=" + this.values + "}"; } }