/*
Copyright 2008-2010 Gephi
Authors : Martin Škurla <bujacik@gmail.com>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.data.attributes.type;
import java.util.Arrays;
/**
* Complex type that defines list of any type of items. Can be created from an array or from single
* string using either given or default separators. Internal representation of data is array of generic
* type. This means that every primitive type must be first converted into wrapper type. The exact
* conversion process from String value into given type is done by {@link TypeConvertor TypeConvertor}
* class.
*
* <br/><br/>
* <h3>Design guidelines</h3>
* This is a basic abstract class that every other 'List' class should extend. In order to not misuse
* the API, every extending type should be one of the following:
* <ul>
* <li>helper type which restricts the type parameter and possibly brings some new functionality (e.g.
* {@link NumberList NumberList}). This is not final usable type so it should be declared as abstract.
* <li>final type that extends any of defined helper types or basic class and sets the type parameter
* (e.g. there are types for representing all primitive types, String, BigInteger & BigDecimal).
* These are final usable types so they should be declared as final.
* </ul>
*
* <h3>Flexibility</h3>
* The flexibility of this API is done in 2 ways:
* <ul>
* <li>We can add functionality by defining conversions from any other type in difference from general
* supported types through defining new constructors (e.g. {@link StringList StringList} class can
* be created from array of characters). We can also restrict the functionality (e.g. BigInteger &
* BigDecimal cannot be created from arrays of primitive types). The conversion process should be
* done by {@link TypeConvertor TypeConvertor} type if the conversion can be used in more List
* implementations or by 'private static T parseXXX()' method in appropriate List implementation if
* only this type uses the conversion (e.g. {@link StringList#parse}).
* <li>Any other functionality required from 'List' implementations should be done by implementing
* appropriate non-static methods in concrete 'List' implementations.
* </ul>
*
* <h3>Extensibility</h3>
* This API can be simply extended. New 'List' type should extend base or any helper 'List' type. We can
* create final 'List' implementations as well as helper 'list' implementations with appropriate modifiers
* (see Design Guidelines). We can define as many constructors responsible for conversions from other
* types and as many additional methods as we want.<br/>
* To fully integrate new 'List' type into the whole codebase we have to update following types:
* <ol>
* <li>{@link org.gephi.data.attributes.api.AttributeType AttributeType}:
* <ol>
* <li>add appropriate enum constants
* <li>update {@link org.gephi.data.attributes.api.AttributeType#parse(String str) parse(String)} method
* </ol>
* <li>{@link org.gephi.data.attributes.model.DataIndex DataIndex}:
* <ol>
* <li>add appropriate type represented by Class object into
* {@link org.gephi.data.attributes.model.DataIndex#SUPPORTED_TYPES SUPPORTED_TYPES} array
* </ol>
* </ol>
*
* This class defines {@link #size method for recognizing size} of the list and
* {@link #getItem method for getting item by index}.
*
* @param <T> type parameter defining final List type
*
* @author Martin Škurla
*
* @see TypeConvertor
*/
public abstract class AbstractList<T> {
public static final String DEFAULT_SEPARATOR = ",|;";
protected final T[] list;
private volatile int hashCode = 0;
public AbstractList(String input, Class<T> finalType) {
this(input, DEFAULT_SEPARATOR, finalType);
}
public AbstractList(String input, String separator, Class<T> finalType) {
this(TypeConvertor.<T>createArrayFromString(input, separator, finalType));
}
public AbstractList(T[] array) {
this.list = Arrays.copyOf(array, array.length);
}
public int size() {
return list.length;
}
public T getItem(int index) {
if (index >= list.length) {
return null;
}
return list[index];
}
public boolean contains(T value) {
for (int i = 0; i < list.length; i++) {
if (list[i].equals(value)) {
return true;
}
}
return false;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < list.length; i++) {
builder.append(list[i]);
builder.append(',');
}
if (list.length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof AbstractList<?>)) {
return false;
}
AbstractList<?> s = (AbstractList<?>) obj;
if (s.size() != this.size()) {
return false;
}
for (int i = 0; i < list.length; i++) {
if (this.getItem(i) != s.getItem(i)) {
if (this.getItem(i)!=null&&!this.getItem(i).equals(s.getItem(i))) {
return false;
}else if(s.getItem(i)!=null&&!s.getItem(i).equals(this.getItem(i))){
return false;
}
}
}
return true;
}
@Override
public int hashCode() {
if (hashCode == 0) {
int hash = 7;
for (int i = 0; i < list.length; i++) {
hash = 53 * hash + (this.list[i] != null ? this.list[i].hashCode() : 0);
}
hashCode = hash;
}
return hashCode;
}
}