View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.query.validate;
25  
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.LinkedList;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  import net.jcip.annotations.Immutable;
37  import org.modeshape.graph.query.model.SelectorName;
38  import org.modeshape.graph.query.validate.Schemata.Column;
39  import org.modeshape.graph.query.validate.Schemata.Key;
40  import org.modeshape.graph.query.validate.Schemata.Table;
41  
42  @Immutable
43  class ImmutableTable implements Table {
44      private final SelectorName name;
45      private final Map<String, Column> columnsByName;
46      private final List<Column> columns;
47      private final Set<Key> keys;
48      private final boolean extraColumns;
49      private final List<Column> selectStarColumns;
50      private final Map<String, Column> selectStarColumnsByName;
51  
52      protected ImmutableTable( SelectorName name,
53                                Iterable<Column> columns,
54                                boolean extraColumns ) {
55          this(name, columns, extraColumns, (Iterable<Column>[])null);
56      }
57  
58      protected ImmutableTable( SelectorName name,
59                                Iterable<Column> columns,
60                                boolean extraColumns,
61                                Iterable<Column>... keyColumns ) {
62          this.name = name;
63          // Define the columns ...
64          List<Column> columnList = new ArrayList<Column>();
65          Map<String, Column> columnMap = new HashMap<String, Column>();
66          for (Column column : columns) {
67              Column old = columnMap.put(column.getName(), column);
68              if (old != null) {
69                  columnList.set(columnList.indexOf(old), column);
70              } else {
71                  columnList.add(column);
72              }
73          }
74          this.columnsByName = Collections.unmodifiableMap(columnMap);
75          this.columns = Collections.unmodifiableList(columnList);
76          // Define the keys ...
77          if (keyColumns != null) {
78              Set<Key> keys = new HashSet<Key>();
79              for (Iterable<Column> keyColumnSet : keyColumns) {
80                  if (keyColumnSet != null) {
81                      Key key = new ImmutableKey(keyColumnSet);
82                      keys.add(key);
83                  }
84              }
85              this.keys = Collections.unmodifiableSet(keys);
86          } else {
87              this.keys = Collections.emptySet();
88          }
89          this.extraColumns = extraColumns;
90          this.selectStarColumns = this.columns;
91          this.selectStarColumnsByName = this.columnsByName;
92      }
93  
94      protected ImmutableTable( SelectorName name,
95                                Map<String, Column> columnsByName,
96                                List<Column> columns,
97                                Set<Key> keys,
98                                boolean extraColumns,
99                                Map<String, Column> selectStarColumnsByName,
100                               List<Column> selectStarColumns ) {
101         this.name = name;
102         this.columns = columns;
103         this.columnsByName = columnsByName;
104         this.keys = keys;
105         this.extraColumns = extraColumns;
106         assert selectStarColumns != null;
107         assert selectStarColumnsByName != null;
108         assert selectStarColumns.size() == selectStarColumnsByName.size();
109         this.selectStarColumns = selectStarColumns;
110         this.selectStarColumnsByName = selectStarColumnsByName;
111     }
112 
113     /**
114      * {@inheritDoc}
115      * 
116      * @see org.modeshape.graph.query.validate.Schemata.Table#getName()
117      */
118     public SelectorName getName() {
119         return name;
120     }
121 
122     /**
123      * {@inheritDoc}
124      * 
125      * @see org.modeshape.graph.query.validate.Schemata.Table#getColumn(java.lang.String)
126      */
127     public Column getColumn( String name ) {
128         return columnsByName.get(name);
129     }
130 
131     /**
132      * {@inheritDoc}
133      * 
134      * @see org.modeshape.graph.query.validate.Schemata.Table#getColumns()
135      */
136     public List<Column> getColumns() {
137         return columns;
138     }
139 
140     /**
141      * {@inheritDoc}
142      * 
143      * @see org.modeshape.graph.query.validate.Schemata.Table#getSelectAllColumns()
144      */
145     public List<Column> getSelectAllColumns() {
146         return selectStarColumns;
147     }
148 
149     /**
150      * {@inheritDoc}
151      * 
152      * @see org.modeshape.graph.query.validate.Schemata.Table#getSelectAllColumnsByName()
153      */
154     public Map<String, Column> getSelectAllColumnsByName() {
155         return selectStarColumnsByName;
156     }
157 
158     /**
159      * {@inheritDoc}
160      * 
161      * @see org.modeshape.graph.query.validate.Schemata.Table#getColumnsByName()
162      */
163     public Map<String, Column> getColumnsByName() {
164         return columnsByName;
165     }
166 
167     /**
168      * {@inheritDoc}
169      * 
170      * @see org.modeshape.graph.query.validate.Schemata.Table#getKeys()
171      */
172     public Collection<Key> getKeys() {
173         return keys;
174     }
175 
176     protected Set<Key> getKeySet() {
177         return keys;
178     }
179 
180     /**
181      * {@inheritDoc}
182      * 
183      * @see org.modeshape.graph.query.validate.Schemata.Table#getKey(org.modeshape.graph.query.validate.Schemata.Column[])
184      */
185     public Key getKey( Column... columns ) {
186         for (Key key : keys) {
187             if (key.hasColumns(columns)) return key;
188         }
189         return null;
190     }
191 
192     /**
193      * {@inheritDoc}
194      * 
195      * @see org.modeshape.graph.query.validate.Schemata.Table#getKey(java.lang.Iterable)
196      */
197     public Key getKey( Iterable<Column> columns ) {
198         for (Key key : keys) {
199             if (key.hasColumns(columns)) return key;
200         }
201         return null;
202     }
203 
204     /**
205      * {@inheritDoc}
206      * 
207      * @see org.modeshape.graph.query.validate.Schemata.Table#hasKey(org.modeshape.graph.query.validate.Schemata.Column[])
208      */
209     public boolean hasKey( Column... columns ) {
210         return getKey(columns) != null;
211     }
212 
213     /**
214      * {@inheritDoc}
215      * 
216      * @see org.modeshape.graph.query.validate.Schemata.Table#hasKey(java.lang.Iterable)
217      */
218     public boolean hasKey( Iterable<Column> columns ) {
219         return getKey(columns) != null;
220     }
221 
222     /**
223      * {@inheritDoc}
224      * 
225      * @see org.modeshape.graph.query.validate.Schemata.Table#hasExtraColumns()
226      */
227     public boolean hasExtraColumns() {
228         return extraColumns;
229     }
230 
231     public ImmutableTable withColumn( String name,
232                                       String type ) {
233         return withColumn(name, type, ImmutableColumn.DEFAULT_FULL_TEXT_SEARCHABLE);
234     }
235 
236     public ImmutableTable withColumn( String name,
237                                       String type,
238                                       boolean fullTextSearchable ) {
239         // Create the new column ...
240         Column newColumn = new ImmutableColumn(name, type, fullTextSearchable);
241         // Add to the list and map ...
242         List<Column> newColumns = new LinkedList<Column>(columns);
243         newColumns.add(newColumn);
244         List<Column> selectStarColumns = new LinkedList<Column>(this.selectStarColumns);
245         Map<String, Column> selectStarColumnMap = new HashMap<String, Column>(this.selectStarColumnsByName);
246         Map<String, Column> columnMap = new HashMap<String, Column>(columnsByName);
247         Column existing = columnMap.put(newColumn.getName(), newColumn);
248         if (existing != null) {
249             newColumns.remove(existing);
250             if (selectStarColumnMap.containsKey(existing.getName())) {
251                 // The old column was in the SELECT * list, so the new one should be, too...
252                 selectStarColumnMap.put(newColumn.getName(), newColumn);
253                 selectStarColumns.add(newColumn);
254             }
255         }
256         return new ImmutableTable(getName(), columnMap, newColumns, keys, extraColumns, selectStarColumnMap, selectStarColumns);
257     }
258 
259     public ImmutableTable withColumns( Iterable<Column> columns ) {
260         // Add to the list and map ...
261         List<Column> newColumns = new LinkedList<Column>(this.getColumns());
262         List<Column> selectStarColumns = new LinkedList<Column>(this.selectStarColumns);
263         Map<String, Column> selectStarColumnMap = new HashMap<String, Column>(this.selectStarColumnsByName);
264         Map<String, Column> columnMap = new HashMap<String, Column>(columnsByName);
265         for (Column column : columns) {
266             Column newColumn = new ImmutableColumn(column.getName(), column.getPropertyType(), column.isFullTextSearchable());
267             newColumns.add(newColumn);
268             Column existing = columnMap.put(newColumn.getName(), newColumn);
269             if (existing != null) {
270                 newColumns.remove(existing);
271                 if (selectStarColumnMap.containsKey(existing.getName())) {
272                     // The old column was in the SELECT * list, so the new one should be, too...
273                     selectStarColumnMap.put(newColumn.getName(), newColumn);
274                     selectStarColumns.add(newColumn);
275                 }
276             }
277         }
278         return new ImmutableTable(getName(), columnMap, newColumns, keys, extraColumns, selectStarColumnMap, selectStarColumns);
279     }
280 
281     public ImmutableTable with( SelectorName name ) {
282         return new ImmutableTable(name, columnsByName, columns, keys, extraColumns, selectStarColumnsByName, selectStarColumns);
283     }
284 
285     public ImmutableTable withKey( Iterable<Column> keyColumns ) {
286         Set<Key> keys = new HashSet<Key>(this.keys);
287         for (Column keyColumn : keyColumns) {
288             assert columns.contains(keyColumn);
289         }
290         if (!keys.add(new ImmutableKey(keyColumns))) return this;
291         return new ImmutableTable(name, columnsByName, columns, keys, extraColumns, selectStarColumnsByName, selectStarColumns);
292     }
293 
294     public ImmutableTable withKey( Column... keyColumns ) {
295         return withKey(Arrays.asList(keyColumns));
296     }
297 
298     public ImmutableTable withExtraColumns() {
299         return extraColumns ? this : new ImmutableTable(name, columnsByName, columns, keys, true, selectStarColumnsByName,
300                                                         selectStarColumns);
301     }
302 
303     public ImmutableTable withoutExtraColumns() {
304         return !extraColumns ? this : new ImmutableTable(name, columnsByName, columns, keys, false, selectStarColumnsByName,
305                                                          selectStarColumns);
306     }
307 
308     public ImmutableTable withColumnNotInSelectStar( String name ) {
309         Column column = columnsByName.get(name);
310         if (column == null) return this;
311         if (!getSelectAllColumnsByName().containsKey(name)) {
312             return this; // already not in select *
313         }
314         List<Column> selectStarColumns = new LinkedList<Column>(this.selectStarColumns);
315         Map<String, Column> selectStarColumnsByName = new HashMap<String, Column>(this.selectStarColumnsByName);
316         selectStarColumns.remove(column);
317         selectStarColumnsByName.remove(name);
318         return new ImmutableTable(this.name, columnsByName, columns, keys, extraColumns, selectStarColumnsByName,
319                                   selectStarColumns);
320     }
321 
322     /**
323      * {@inheritDoc}
324      * 
325      * @see java.lang.Object#toString()
326      */
327     @Override
328     public String toString() {
329         StringBuilder sb = new StringBuilder(name.name());
330         sb.append('(');
331         boolean first = true;
332         for (Column column : columns) {
333             if (first) first = false;
334             else sb.append(", ");
335             sb.append(column);
336         }
337         sb.append(')');
338         if (!keys.isEmpty()) {
339             sb.append(" with keys ");
340             first = true;
341             for (Key key : keys) {
342                 if (first) first = false;
343                 else sb.append(", ");
344                 sb.append(key);
345             }
346         }
347         return sb.toString();
348     }
349 }