org.integratedmodelling.engine.kbox.sql.h2.schema.CompoundSchema Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa
* - integratedmodelling.org
* - any other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite,
* meant to enable modular, collaborative, integrated
* development of interoperable data and model components. For
* details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Affero General Public License
* Version 3 or 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
* Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* The license is also available at: https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.kbox.sql.h2.schema;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.integratedmodelling.engine.kbox.sql.h2.H2Kbox.Schema;
import org.integratedmodelling.engine.kbox.sql.h2.H2Schema;
/**
* Need:
* a literal schema (no fields)
* a collection schema (with order option and a schema for the collected)
* a map schema
* a compound schema (map field->schema)
*
* @author Ferd
*
*/
public class CompoundSchema extends H2Schema {
List children = new ArrayList<>();
public CompoundSchema(Class cls) {
super(cls);
extractSchema(cls);
sqlType = Types.OTHER;
}
@Override
public String getCreateSQL() {
ArrayList statements = new ArrayList<>();
getCreateSQLInternal(statements);
return StringUtils.join(statements, ' ');
}
protected void getCreateSQLInternal(List statements) {
Collections.sort(children, new Comparator() {
@Override
public int compare(Schema arg0, Schema arg1) {
return arg0.getFieldName().compareTo(arg1.getFieldName());
}
});
String statement =
"CREATE TABLE " + getTableName() + " (" +
H2Schema.FIELD_PKEY + " LONG PRIMARY KEY," +
H2Schema.FIELD_FKEY + " LONG";
int nFields = 0;
for (Schema s : children) {
if (s instanceof CompoundSchema) {
((CompoundSchema) s).getCreateSQLInternal(statements);
} else {
statement += ", " + s.getFieldName() + " " + s.getFieldType();
nFields++;
}
}
/*
* finish statement
*/
statements.add(statement + ");");
}
@Override
public boolean equals(Object o) {
return o instanceof H2Schema && compareFields((H2Schema) o);
}
private boolean compareFields(H2Schema o) {
// TODO Auto-generated method stub
return false;
}
private void extractSchema(Class cls) {
for (Field field : getAssignableFields(cls)) {
// children.add(((H2Schema) SchemaFactory.getSchema(field)).named(sanitizeName(field.getName())));
}
}
/**
* Assignable fields have properties associated, either through annotation or by
* naming convention.
*
* @param cls
* @return
*/
private Collection getAssignableFields(Class cls) {
return getAssignableFieldsInternal(cls, new ArrayList(), new HashSet());
}
/*
* Collect fields we can use from the class to annotate. Rules are:
* 1. the class (or superclass) must be registered with the
* annotation factory, either directly or through a @Concept annotation;
* 2. if the class has one or more @KboxInclude field annotations, we only check fields
* that have it; otherwise all fields are game except any annotated with @KboxIgnore.
* We may later honor parameters for indexing or other configuration; for now we want
* the simplest possible API usage so we ignore everything but the annotation itself.
* 3. If we use non-annotated fields, their name must map to an existing
* property in the namespace of the concept mapped to the class they're defined in.
* 4. Property names are obtained by removing any leading underscores, capitalizing
* the first letter and prefixing the resulting string with "is" for boolean
* fields and "has" for all others. If the field points to a collection or
* map, a trailing "s" is also removed to make the property a singular. If the
* string contains underscores, those are removed and the segments between
* underscores are capitalized to a nice camelcase syntax, more typical of
* OWL properties.
*/
private Collection getAssignableFieldsInternal(Class cls,
List ret, HashSet done) {
/*
* eventually we get passed null as a superclass.
*/
if (cls == null)
return ret;
/*
* scan parents first. This is done even if our own type isn't registered.
*/
getAssignableFieldsInternal(cls.getSuperclass(), ret, done);
boolean useAnnotation = false;
for (Field f : cls.getDeclaredFields()) {
// ignore static fields anyway
if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
continue;
}
// if (f.isAnnotationPresent(KboxInclude.class)) {
// useAnnotation = true;
// break;
// }
}
/*
* now go collect them
*/
for (Field f : cls.getDeclaredFields()) {
if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
continue;
}
if (done.contains(f))
continue;
done.add(f);
// if ((useAnnotation && f.isAnnotationPresent(KboxInclude.class))
// || (!useAnnotation && !f.isAnnotationPresent(KboxIgnore.class))) {
// ret.add(f);
// }
}
return ret;
}
@Override
public String getTableName() {
return cls.getSimpleName().replace('.', '_');
}
}