/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.q;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.andork.collect.CollectionUtils;
import org.andork.reflect.ReflectionUtils;
public abstract class QSpec<S extends QSpec<S>> {
public static class Attribute<T> {
public static <T> Attribute<T> newInstance(Class<? super T> valueClass, String name) {
return new Attribute<T>(valueClass, name);
}
final Class<? super T> valueClass;
int index = -1;
final String name;
public Attribute(Class<? super T> valueClass, String name) {
super();
this.valueClass = valueClass;
this.name = name;
}
public int getIndex() {
return index;
}
public String getName() {
return name;
}
public Class<? super T> getValueClass() {
return valueClass;
}
@Override
public String toString() {
return getName();
}
}
public static <T> Attribute<T> newAttribute(Class<? super T> valueClass, String name) {
return new Attribute<T>(valueClass, name);
}
public static <S extends QSpec<S>> Attribute<QObject<S>> newAttribute(S spec, String name) {
return new Attribute<QObject<S>>(QObject.class, name);
}
final Attribute<?>[] attributes;
final List<Attribute<?>> attributeList;
final Map<String, Attribute<?>> attributesByName = new LinkedHashMap<String, Attribute<?>>();
protected QSpec() {
for (Field field : ReflectionUtils.getStaticFieldList(getClass(), true)) {
if (field.getType() == Attribute.class) {
try {
field.setAccessible(true);
Attribute<?> attr = (Attribute<?>) field.get(null);
attributesByName.put(attr.getName(), attr);
} catch (Exception ex) {
// Shouldn't happen...
}
}
}
this.attributes = attributesByName.values().toArray(new Attribute[attributesByName.size()]);
for (int i = 0; i < attributes.length; i++) {
if (attributes[i].index < 0) {
attributes[i].index = i;
} else if (attributes[i].index != i) {
throw new IllegalStateException("attribute order conflicts with another spec");
}
}
this.attributeList = Collections.unmodifiableList(Arrays.asList(attributes));
}
protected QSpec(Attribute<?>... attributes) {
this(Arrays.asList(attributes));
}
protected QSpec(Iterable<Attribute<?>> attrIterable) {
for (Attribute<?> attr : attrIterable) {
attributesByName.put(attr.getName(), attr);
}
ArrayList<Attribute<?>> attrList = CollectionUtils.toArrayList(attrIterable);
attrList.trimToSize();
attributeList = Collections.unmodifiableList(attrList);
attributes = attributeList.toArray(new Attribute[attributeList.size()]);
for (int i = 0; i < attributeList.size(); i++) {
if (attributes[i].index < 0) {
attributes[i].index = i;
} else if (attributes[i].index != i) {
throw new IllegalArgumentException("attributes[" + i + "].index == " + attributes[i].index);
}
}
}
public Attribute<?> attributeAt(int index) {
return attributes[index];
}
public Attribute<?> getAttribute(String name) {
return attributesByName.get(name);
}
public int getAttributeCount() {
return attributes.length;
}
public List<Attribute<?>> getAttributes() {
return attributeList;
}
public QObject<S> newObject() {
return QObject.newInstance((S) this);
}
}