/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr.query.validate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.modeshape.common.annotation.Immutable; import org.modeshape.jcr.query.model.SelectorName; import org.modeshape.jcr.query.validate.Schemata.Column; import org.modeshape.jcr.query.validate.Schemata.Key; import org.modeshape.jcr.query.validate.Schemata.Table; @Immutable class ImmutableTable implements Table { private final SelectorName name; private final Map<String, Column> columnsByName; private final List<Column> columns; private final Set<Key> keys; private final boolean extraColumns; private final List<Column> selectStarColumns; private final Map<String, Column> selectStarColumnsByName; protected ImmutableTable( SelectorName name, Iterable<Column> columns, boolean extraColumns ) { this(name, columns, extraColumns, (Iterable<Column>[])null); } @SafeVarargs protected ImmutableTable( SelectorName name, Iterable<Column> columns, boolean extraColumns, Iterable<Column>... keyColumns ) { this.name = name; // Define the columns ... List<Column> columnList = new ArrayList<Column>(); Map<String, Column> columnMap = new HashMap<String, Column>(); for (Column column : columns) { Column old = columnMap.put(column.getName(), column); if (old != null) { columnList.set(columnList.indexOf(old), column); } else { columnList.add(column); } } this.columnsByName = Collections.unmodifiableMap(columnMap); this.columns = Collections.unmodifiableList(columnList); // Define the keys ... if (keyColumns != null) { Set<Key> keys = new HashSet<Key>(); for (Iterable<Column> keyColumnSet : keyColumns) { if (keyColumnSet != null) { Key key = new ImmutableKey(keyColumnSet); keys.add(key); } } this.keys = Collections.unmodifiableSet(keys); } else { this.keys = Collections.emptySet(); } this.extraColumns = extraColumns; this.selectStarColumns = this.columns; this.selectStarColumnsByName = this.columnsByName; } protected ImmutableTable( SelectorName name, Map<String, Column> columnsByName, List<Column> columns, Set<Key> keys, boolean extraColumns, Map<String, Column> selectStarColumnsByName, List<Column> selectStarColumns ) { this.name = name; this.columns = columns; this.columnsByName = columnsByName; this.keys = keys; this.extraColumns = extraColumns; assert selectStarColumns != null; assert selectStarColumnsByName != null; assert selectStarColumns.size() == selectStarColumnsByName.size(); this.selectStarColumns = selectStarColumns; this.selectStarColumnsByName = selectStarColumnsByName; } @Override public SelectorName getName() { return name; } @Override public Column getColumn( String name ) { return columnsByName.get(name); } @Override public List<Column> getColumns() { return columns; } @Override public List<Column> getSelectAllColumns() { return selectStarColumns; } @Override public Map<String, Column> getSelectAllColumnsByName() { return selectStarColumnsByName; } @Override public Map<String, Column> getColumnsByName() { return columnsByName; } @Override public Collection<Key> getKeys() { return keys; } protected Set<Key> getKeySet() { return keys; } @Override public Key getKey( Column... columns ) { for (Key key : keys) { if (key.hasColumns(columns)) return key; } return null; } @Override public Key getKey( Iterable<Column> columns ) { for (Key key : keys) { if (key.hasColumns(columns)) return key; } return null; } @Override public boolean hasKey( Column... columns ) { return getKey(columns) != null; } @Override public boolean hasKey( Iterable<Column> columns ) { return getKey(columns) != null; } @Override public boolean hasExtraColumns() { return extraColumns; } public ImmutableTable withColumns( Iterable<Column> columns ) { // Add to the list and map ... List<Column> newColumns = new LinkedList<Column>(this.getColumns()); List<Column> selectStarColumns = new LinkedList<Column>(this.selectStarColumns); Map<String, Column> selectStarColumnMap = new HashMap<String, Column>(this.selectStarColumnsByName); Map<String, Column> columnMap = new HashMap<String, Column>(columnsByName); for (Column column : columns) { Column newColumn = new ImmutableColumn(column.getName(), column.getPropertyTypeName(), column.getRequiredType(), column.isFullTextSearchable(), column.isOrderable(), column.getMinimum(), column.getMaximum(), column.getOperators()); newColumns.add(newColumn); Column existing = columnMap.put(newColumn.getName(), newColumn); if (existing != null) { newColumns.remove(existing); if (selectStarColumnMap.containsKey(existing.getName())) { // The old column was in the SELECT * list, so the new one should be, too... selectStarColumnMap.put(newColumn.getName(), newColumn); selectStarColumns.add(newColumn); } } } return new ImmutableTable(getName(), columnMap, newColumns, keys, extraColumns, selectStarColumnMap, selectStarColumns); } public ImmutableTable with( SelectorName name ) { return new ImmutableTable(name, columnsByName, columns, keys, extraColumns, selectStarColumnsByName, selectStarColumns); } public ImmutableTable withKey( Iterable<Column> keyColumns ) { Set<Key> keys = new HashSet<Key>(this.keys); for (Column keyColumn : keyColumns) { assert columns.contains(keyColumn); } if (!keys.add(new ImmutableKey(keyColumns))) return this; return new ImmutableTable(name, columnsByName, columns, keys, extraColumns, selectStarColumnsByName, selectStarColumns); } public ImmutableTable withKey( Column... keyColumns ) { return withKey(Arrays.asList(keyColumns)); } public ImmutableTable withExtraColumns() { return extraColumns ? this : new ImmutableTable(name, columnsByName, columns, keys, true, selectStarColumnsByName, selectStarColumns); } public ImmutableTable withoutExtraColumns() { return !extraColumns ? this : new ImmutableTable(name, columnsByName, columns, keys, false, selectStarColumnsByName, selectStarColumns); } public ImmutableTable withColumnNotInSelectStar( String name ) { Column column = columnsByName.get(name); if (column == null) return this; if (!getSelectAllColumnsByName().containsKey(name)) { return this; // already not in select * } List<Column> selectStarColumns = new LinkedList<Column>(this.selectStarColumns); Map<String, Column> selectStarColumnsByName = new HashMap<String, Column>(this.selectStarColumnsByName); selectStarColumns.remove(column); selectStarColumnsByName.remove(name); return new ImmutableTable(this.name, columnsByName, columns, keys, extraColumns, selectStarColumnsByName, selectStarColumns); } @Override public String toString() { StringBuilder sb = new StringBuilder(name.name()); sb.append('('); boolean first = true; for (Column column : columns) { if (first) first = false; else sb.append(", "); sb.append(column); } sb.append(')'); if (!keys.isEmpty()) { sb.append(" with keys "); first = true; for (Key key : keys) { if (first) first = false; else sb.append(", "); sb.append(key); } } return sb.toString(); } }