View Javadoc
1   /*
2   Copyright (c) 2016 James Ahlborn
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess.impl;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Set;
22  
23  import com.healthmarketscience.jackcess.ColumnBuilder;
24  import com.healthmarketscience.jackcess.DataType;
25  import com.healthmarketscience.jackcess.IndexBuilder;
26  
27  /**
28   * Common helper class used to maintain state during table mutation.
29   *
30   * @author James Ahlborn
31   * @usage _advanced_class_
32   */
33  public abstract class TableMutator extends DBMutator
34  {
35    private ColumnOffsets _colOffsets;
36  
37    protected TableMutator(DatabaseImpl database) {
38      super(database);
39    }
40  
41    public void setColumnOffsets(
42        int fixedOffset, int varOffset, int longVarOffset) {
43      if(_colOffsets == null) {
44        _colOffsets = new ColumnOffsets();
45      }
46      _colOffsets.set(fixedOffset, varOffset, longVarOffset);
47    }
48  
49    public ColumnOffsets getColumnOffsets() {
50      return _colOffsets;
51    }
52  
53    public IndexImpl.ForeignKeyReference getForeignKey(IndexBuilder idx) {
54      return null;
55    }
56  
57    protected void validateColumn(Set<String> colNames, ColumnBuilder column) {
58  
59        // FIXME for now, we can't create complex columns
60        if(column.getType() == DataType.COMPLEX_TYPE) {
61          throw new UnsupportedOperationException(withErrorContext(
62              "Complex column creation is not yet implemented"));
63        }
64        
65        column.validate(getFormat());
66        if(!colNames.add(DatabaseImpl.toLookupName(column.getName()))) {
67          throw new IllegalArgumentException(withErrorContext(
68              "duplicate column name: " + column.getName()));
69        }
70  
71        setColumnSortOrder(column);
72    }
73  
74    protected void validateIndex(Set<String> colNames, Set<String> idxNames, 
75                                 boolean[] foundPk, IndexBuilder index) {
76      
77      index.validate(colNames, getFormat());
78      if(!idxNames.add(DatabaseImpl.toLookupName(index.getName()))) {
79        throw new IllegalArgumentException(withErrorContext(
80            "duplicate index name: " + index.getName()));
81      }
82      if(index.isPrimaryKey()) {
83        if(foundPk[0]) {
84          throw new IllegalArgumentException(withErrorContext(
85              "found second primary key index: " + index.getName()));
86        }
87        foundPk[0] = true;
88      } else if(index.getType() == IndexImpl.FOREIGN_KEY_INDEX_TYPE) {
89        if(getForeignKey(index) == null) {
90          throw new IllegalArgumentException(withErrorContext(
91              "missing foreign key info for " + index.getName()));
92        }
93      }
94    }
95  
96    protected void validateAutoNumberColumn(Set<DataType> autoTypes, 
97                                            ColumnBuilder column) 
98    {
99      if(!column.getType().isMultipleAutoNumberAllowed() &&
100        !autoTypes.add(column.getType())) {
101       throw new IllegalArgumentException(withErrorContext(
102           "Can have at most one AutoNumber column of type " + column.getType() +
103           " per table"));
104     }    
105   }
106 
107   private void setColumnSortOrder(ColumnBuilder column) {
108       // set the sort order to the db default (if unspecified)
109       if(column.getType().isTextual() && (column.getTextSortOrder() == null)) {
110         column.setTextSortOrder(getDbSortOrder());
111       }
112   }
113 
114   abstract String getTableName();
115 
116   public abstract int getTdefPageNumber();
117 
118   abstract short getColumnNumber(String colName);
119 
120   public abstract ColumnState getColumnState(ColumnBuilder col);
121 
122   public abstract IndexDataState getIndexDataState(IndexBuilder idx);
123 
124   protected abstract String withErrorContext(String msg);
125 
126   /**
127    * Maintains additional state used during column writing.
128    * @usage _advanced_class_
129    */
130   static final class ColumnOffsets
131   {
132     private short _fixedOffset;
133     private short _varOffset;
134     private short _longVarOffset;
135 
136     public void set(int fixedOffset, int varOffset, int longVarOffset) {
137       _fixedOffset = (short)fixedOffset;
138       _varOffset = (short)varOffset;
139       _longVarOffset = (short)longVarOffset;
140     }
141     
142     public short getNextVariableOffset(ColumnBuilder col) {
143       if(!col.getType().isLongValue()) {
144         return _varOffset++;
145       }
146       return _longVarOffset++;
147     }
148 
149     public short getNextFixedOffset(ColumnBuilder col) {
150       short offset = _fixedOffset;
151       _fixedOffset += col.getType().getFixedSize(col.getLength());
152       return offset;
153     }
154   }
155 
156   /**
157    * Maintains additional state used during column creation.
158    * @usage _advanced_class_
159    */
160   static final class ColumnState
161   {
162     private byte _umapOwnedRowNumber;
163     private byte _umapFreeRowNumber;
164     // we always put both usage maps on the same page
165     private int _umapPageNumber;
166 
167     public byte getUmapOwnedRowNumber() {
168       return _umapOwnedRowNumber;
169     }
170 
171     public void setUmapOwnedRowNumber(byte newUmapOwnedRowNumber) {
172       _umapOwnedRowNumber = newUmapOwnedRowNumber;
173     }
174 
175     public byte getUmapFreeRowNumber() {
176       return _umapFreeRowNumber;
177     }
178 
179     public void setUmapFreeRowNumber(byte newUmapFreeRowNumber) {
180       _umapFreeRowNumber = newUmapFreeRowNumber;
181     }
182 
183     public int getUmapPageNumber() {
184       return _umapPageNumber;
185     }
186 
187     public void setUmapPageNumber(int newUmapPageNumber) {
188       _umapPageNumber = newUmapPageNumber;
189     }
190   }
191 
192   /**
193    * Maintains additional state used during index data creation.
194    * @usage _advanced_class_
195    */
196   static final class IndexDataState
197   {
198     private final List<IndexBuilder> _indexes = new ArrayList<IndexBuilder>();
199     private int _indexDataNumber;
200     private byte _umapRowNumber;
201     private int _umapPageNumber;
202     private int _rootPageNumber;
203 
204     public IndexBuilder getFirstIndex() {
205       // all indexes which have the same backing IndexDataState will have
206       // equivalent columns and flags.
207       return _indexes.get(0);
208     }
209 
210     public List<IndexBuilder> getIndexes() {
211       return _indexes;
212     }
213 
214     public void addIndex(IndexBuilder idx) {
215       _indexes.add(idx);
216     }
217 
218     public int getIndexDataNumber() {
219       return _indexDataNumber;
220     }
221 
222     public void setIndexDataNumber(int newIndexDataNumber) {
223       _indexDataNumber = newIndexDataNumber;
224     }
225 
226     public byte getUmapRowNumber() {
227       return _umapRowNumber;
228     }
229 
230     public void setUmapRowNumber(byte newUmapRowNumber) {
231       _umapRowNumber = newUmapRowNumber;
232     }
233 
234     public int getUmapPageNumber() {
235       return _umapPageNumber;
236     }
237 
238     public void setUmapPageNumber(int newUmapPageNumber) {
239       _umapPageNumber = newUmapPageNumber;
240     }
241 
242     public int getRootPageNumber() {
243       return _rootPageNumber;
244     }
245 
246     public void setRootPageNumber(int newRootPageNumber) {
247       _rootPageNumber = newRootPageNumber;
248     }
249   }    
250 }