/* * Copyright (C) 2010 eXo Platform SAS. * * 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 2.1 of the License, or (at your option) * any later version. * * This software 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. * * You should have received a copy of the GNU Lesser General Public License * along with this software; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF * site: http://www.fsf.org. */ package org.xcmis.search.content; import org.apache.commons.lang.Validate; import org.xcmis.search.model.Query; import org.xcmis.search.model.constraint.Operator; import org.xcmis.search.model.source.SelectorName; import org.xcmis.search.value.PropertyType; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * In memory {@link Schema} implementation. */ public class InMemorySchema implements Schema { /** * Obtain a new instance for building Schema objects. * * @return the new builder; never null */ public static Builder createBuilder() { return new Builder(); } /** * A builder of immutable {@link Schema} objects. */ public static class Builder { private final Map<SelectorName, InMemoryTable> tables = new HashMap<SelectorName, InMemoryTable>(); private final Map<SelectorName, Query> viewDefinitions = new HashMap<SelectorName, Query>(); protected Builder() { } /** * Add a table with the supplied name and column names. Each column will * be given a default type. The table will also overwrite any existing * table definition with the same name. * * @param name * the name of the new table * @param columnNames * the names of the columns. * @return this builder, for convenience in method chaining; never null */ public Builder addTable(String name, String... columnNames) { Validate.notEmpty(name, " name may not be empty"); Validate.notEmpty(columnNames, "columnNames may not be empty"); List<Column> columns = new ArrayList<Column>(); int i = 0; for (String columnName : columnNames) { Validate.notEmpty(columnName, "columnName[" + (i++) + "] may not be empty");; // TODO default type columns.add(new InMemoryColumn(columnName, PropertyType.STRING)); } InMemoryTable table = new InMemoryTable(new SelectorName(name), columns); tables.put(table.getName(), table); return this; } /** * Add a table with the supplied name and column names and types. The * table will also overwrite any existing table definition with the same * name. * * @param name * the name of the new table * @param columnNames * the names of the columns * @param types * the types for the columns * @return this builder, for convenience in method chaining; never null */ public Builder addTable(String name, String[] columnNames, PropertyType[] types) { Validate.notEmpty(name, " name may not be empty"); Validate.notEmpty(columnNames, " columnNames may not be empty"); Validate.notEmpty(types, " types may not be empty"); Validate.isTrue(columnNames.length == types.length, "columnNames.length should be equal types.length"); List<Column> columns = new ArrayList<Column>(); assert columnNames.length == types.length; for (int i = 0; i != columnNames.length; ++i) { String columnName = columnNames[i]; Validate.notEmpty(columnName, " columnName[" + i + "] may not be empty"); columns.add(new InMemoryColumn(columnName, types[i])); } InMemoryTable table = new InMemoryTable(new SelectorName(name), columns); tables.put(table.getName(), table); return this; } /** * Add a column with the supplied name and type to the named table. Any * existing column with that name will be replaced with the new column. If * the table does not yet exist, it will be added. * * @param tableName * the name of the new table * @param columnName * the names of the column * @param type * the type for the column * @return this builder, for convenience in method chaining; never null */ public Builder addColumn(String tableName, String columnName, PropertyType type) { Validate.notEmpty(tableName, " tableName may not be empty"); Validate.notEmpty(columnName, " columnName may not be empty"); Validate.notNull(type, " type may not be null"); return addColumn(tableName, columnName, type, InMemoryColumn.DEFAULT_FULL_TEXT_SEARCHABLE, Operator.ALL); } /** * Add a column with the supplied name and type to the named table. Any * existing column with that name will be replaced with the new column. If * the table does not yet exist, it will be added. * * @param tableName * the name of the new table * @param columnName * the names of the column * @param type * the type for the column * @param fullTextSearchable * true if the column should be full-text searchable, or false * if not * @return this builder, for convenience in method chaining; never null */ public Builder addColumn(String tableName, String columnName, PropertyType type, boolean fullTextSearchable, Operator[] availableQueryOperators) { Validate.notEmpty(tableName, " tableName may not be empty"); Validate.notEmpty(columnName, " columnName may not be empty"); Validate.notNull(type, " type may not be null"); SelectorName selector = new SelectorName(tableName); InMemoryTable existing = tables.get(selector); InMemoryTable table = null; if (existing == null) { List<Column> columns = new ArrayList<Column>(); columns.add(new InMemoryColumn(columnName, type, fullTextSearchable, availableQueryOperators)); table = new InMemoryTable(selector, columns); } else { table = existing.withColumn(columnName, type, fullTextSearchable, availableQueryOperators); } tables.put(table.getName(), table); return this; } /** * Make sure the column on the named table is searchable. * * @param tableName * the name of the new table * @param columnName * the names of the column * @return this builder, for convenience in method chaining; never null */ public Builder makeSearchable(String tableName, String columnName) { Validate.notEmpty(tableName, " tableName may not be empty"); Validate.notEmpty(columnName, " columnName may not be empty"); SelectorName selector = new SelectorName(tableName); InMemoryTable existing = tables.get(selector); InMemoryTable table = null; if (existing == null) { List<Column> columns = new ArrayList<Column>(); // TODO default type columns.add(new InMemoryColumn(columnName, PropertyType.STRING, true, Operator.ALL)); table = new InMemoryTable(selector, columns); } else { Column column = existing.getColumn(columnName); // TODO default type PropertyType type = PropertyType.STRING; if (column != null) { type = column.getPropertyType(); } table = existing.withColumn(columnName, type, true, column.getAvailableQueryOperators()); } tables.put(table.getName(), table); return this; } /** * Build the {@link Schema} instance, using the current state of the * builder. This method creates a snapshot of the tables (with their * columns) as they exist at the moment this method is called. * * @return the new Schema; never null */ public Schema build() { InMemorySchema schemata = new InMemorySchema(new HashMap<SelectorName, Table>(tables)); return schemata; } } private final Map<SelectorName, Table> tables; protected InMemorySchema(Map<SelectorName, Table> tables) { this.tables = Collections.unmodifiableMap(tables); } /** * {@inheritDoc} * * @see org.modeshape.graph.query.validate.Schema#getTable(org.modeshape.graph.query.model.SelectorName) */ public Table getTable(SelectorName name) { return tables.get(name); } public InMemorySchema with(Table table) { Map<SelectorName, Table> tables = new HashMap<SelectorName, Table>(this.tables); tables.put(table.getName(), table); return new InMemorySchema(tables); } /** * {@inheritDoc} * * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder sb = new StringBuilder(); boolean first = true; for (Table table : tables.values()) { if (first) { first = false; } else { sb.append('\n'); } sb.append(table); } return sb.toString(); } }