]> source.dussan.org Git - jackcess.git/commitdiff
Add the ability to create tables with calculated fields
authorJames Ahlborn <jtahlborn@yahoo.com>
Sat, 13 Sep 2014 20:02:57 +0000 (20:02 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Sat, 13 Sep 2014 20:02:57 +0000 (20:02 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@873 f203690c-595d-4dc9-a70b-905162fa7fd2

src/changes/changes.xml
src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
src/main/java/com/healthmarketscience/jackcess/TableBuilder.java
src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
src/main/java/com/healthmarketscience/jackcess/impl/JetFormat.java

index 8ef8f7e0160ac0fa02bb94afe786fc0789d7ce92..6b41e9cda349fa6c365d89383c8ef770cf7c0008 100644 (file)
@@ -18,6 +18,9 @@
         Add the ability to set properties in DatabaseBuilder, TableBuilder,
         and ColumnBuilder.
       </action>
+      <action dev="jahlborn" type="add">
+        Add the ability to create tables with calculated fields.
+      </action>
     </release>
     <release version="2.0.4" date="2014-04-05">
       <action dev="jahlborn" type="add">
index 57c5bb5eafe1cd0f3bc5d6afb3f8f9ffdaad1d25..628df86c37a35a8938599837244813d780d8bd33 100644 (file)
@@ -63,6 +63,8 @@ public class ColumnBuilder {
   private boolean _autoNumber;
   /** whether or not the column allows compressed unicode */
   private boolean _compressedUnicode;
+  /** whether or not the column is calculated */
+  private boolean _calculated;
   /** whether or not the column is a hyperlink (memo only) */
   private boolean _hyperlink;
   /** 0-based column number */
@@ -191,6 +193,28 @@ public class ColumnBuilder {
     return _compressedUnicode;
   }
 
+  /**
+   * Sets whether of not the new column is a calculated column.
+   */
+  public ColumnBuilder setCalculated(boolean calculated) {
+    _calculated = calculated;
+    return this;
+  }
+
+  public boolean isCalculated() {
+    return _calculated;
+  }
+
+  /**
+   * Convenience method to set the various info for a calculated type (flag,
+   * result type property and expression)
+   */
+  public ColumnBuilder setCalculatedInfo(String expression) {
+    setCalculated(true);
+    putProperty(PropertyMap.EXPRESSION_PROP, expression);
+    return putProperty(PropertyMap.RESULT_TYPE_PROP, getType().getValue());
+  }
+
   /**
    * Sets whether of not the new column allows unicode compression.
    */
@@ -227,6 +251,10 @@ public class ColumnBuilder {
   public Map<String,PropertyMap.Property> getProperties() {
     return _props;
   }
+
+  private PropertyMap.Property getProperty(String name) {
+    return ((_props != null) ? _props.get(name) : null);
+  }
   
   /**
    * Sets all attributes except name from the given Column template.
@@ -370,6 +398,25 @@ public class ColumnBuilder {
             "Only memo columns can be hyperlinks");
       }
     }
+
+    if(isCalculated()) {
+      if(!format.isSupportedCalculatedDataType(getType())) {
+        throw new IllegalArgumentException(
+            "Database format " + format + " does not support calculated type " +
+            getType());
+      }
+
+      // must have an expression
+      if(getProperty(PropertyMap.EXPRESSION_PROP) == null) {
+        throw new IllegalArgumentException(
+            "No expression provided for calculated type " + getType());
+      }
+
+      // must have result type (just fill in if missing)
+      if(getProperty(PropertyMap.RESULT_TYPE_PROP) == null) {
+        putProperty(PropertyMap.RESULT_TYPE_PROP, getType().getValue());
+      }
+    }
   }
 
   /**
index b28d35b87dccdbfa0dd89d7a9b9f68119e5b5e8e..c1d95f272df216cdf54c21a6b6b60d79c32cfad8 100644 (file)
@@ -212,7 +212,7 @@ public class TableBuilder {
     _props.put(name, PropertyMapImpl.createProperty(name, type, value));
     return this;
   }
-  
+
   /**
    * Creates a new Table in the given Database with the currently configured
    * attributes.
index 4dc1afdbc52818bad61c4ca0f8fba794c408c025..40c8c6c44c3028e2d3a528351ee848d4900e7f46 100644 (file)
@@ -1571,6 +1571,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       buffer.put(col.getType().getValue());
       buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER);  //constant magic number
       buffer.putShort(col.getColumnNumber());  //Column Number
+
       if (col.getType().isVariableLength()) {
         if(!col.getType().isLongValue()) {
           buffer.putShort(variableOffset++);
@@ -1580,13 +1581,16 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       } else {
         buffer.putShort((short) 0);
       }
+
       buffer.putShort(col.getColumnNumber()); //Column Number again
+
       if(col.getType().isTextual()) {
         // this will write 4 bytes (note we don't support writing dbs which
         // use the text code page)
         writeSortOrder(buffer, col.getTextSortOrder(), creator.getFormat());
       } else {
-        if(col.getType().getHasScalePrecision()) {
+        // note scale/precision not stored for calculated numeric fields
+        if(col.getType().getHasScalePrecision() && !col.isCalculated()) {
           buffer.put(col.getPrecision());  // numeric precision
           buffer.put(col.getScale());  // numeric scale
         } else {
@@ -1595,13 +1599,20 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
         }
         buffer.putShort((short) 0); //Unknown
       }
+
       buffer.put(getColumnBitFlags(col)); // misc col flags
-      if (col.isCompressedUnicode()) {  //Compressed
-        buffer.put((byte) 1);
+
+      // note access doesn't seem to allow unicode compression for calced fields
+      if(col.isCalculated()) {
+        buffer.put(CALCULATED_EXT_FLAG_MASK);
+      } else if (col.isCompressedUnicode()) {  //Compressed
+        buffer.put(COMPRESSED_UNICODE_EXT_FLAG_MASK);
       } else {
-        buffer.put((byte) 0);
+        buffer.put((byte)0);
       }
+
       buffer.putInt(0); //Unknown, but always 0.
+
       //Offset for fixed length columns
       if (col.getType().isVariableLength()) {
         buffer.putShort((short) 0);
@@ -1609,11 +1620,13 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
         buffer.putShort(fixedOffset);
         fixedOffset += col.getType().getFixedSize(col.getLength());
       }
+
       if(!col.getType().isLongValue()) {
         buffer.putShort(col.getLength()); //Column length
       } else {
         buffer.putShort((short)0x0000); // unused
       }
+
     }
     for (ColumnBuilder col : columns) {
       TableImpl.writeName(buffer, col.getName(), creator.getCharset());
index ebc9081eb6a74e900c6fae9c49a16eed11b776a2..fcb39bd494649094b87c3232e04902dddead831a 100644 (file)
@@ -32,11 +32,13 @@ import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.charset.Charset;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
-import com.healthmarketscience.jackcess.Database;
 import com.healthmarketscience.jackcess.DataType;
+import com.healthmarketscience.jackcess.Database;
 
 /**
  * Encapsulates constants describing a specific version of the Access Jet format
@@ -139,6 +141,13 @@ public abstract class JetFormat {
     }
   }
 
+  /** calculated types supported in version 14 */
+  private static final Set<DataType> V14_CALC_TYPES = 
+    EnumSet.of(DataType.BOOLEAN, DataType.BYTE, DataType.INT, DataType.LONG,
+               DataType.FLOAT, DataType.DOUBLE, DataType.GUID, 
+               DataType.SHORT_DATE_TIME, DataType.MONEY, DataType.NUMERIC, 
+               DataType.TEXT, DataType.MEMO);
+
   /** the JetFormat constants for the Jet database version "3" */
   public static final JetFormat VERSION_3 = new Jet3Format();
   /** the JetFormat constants for the Jet database version "4" */
@@ -507,6 +516,8 @@ public abstract class JetFormat {
 
   public abstract boolean isSupportedDataType(DataType type);
 
+  public abstract boolean isSupportedCalculatedDataType(DataType type);
+
   @Override
   public String toString() {
     return _name;
@@ -733,6 +744,11 @@ public abstract class JetFormat {
     public boolean isSupportedDataType(DataType type) {
       return (type != DataType.COMPLEX_TYPE);
     }
+
+    @Override
+    public boolean isSupportedCalculatedDataType(DataType type) {
+      return false;
+    }
   }
   
   private static class Jet4Format extends JetFormat {
@@ -958,6 +974,11 @@ public abstract class JetFormat {
     public boolean isSupportedDataType(DataType type) {
       return (type != DataType.COMPLEX_TYPE);
     }
+
+    @Override
+    public boolean isSupportedCalculatedDataType(DataType type) {
+      return false;
+    }
   }
   
   private static final class MSISAMFormat extends Jet4Format {
@@ -1010,6 +1031,11 @@ public abstract class JetFormat {
     public boolean isSupportedDataType(DataType type) {
       return true;
     }
+
+    @Override
+    public boolean isSupportedCalculatedDataType(DataType type) {
+      return false;
+    }
   }
 
   private static final class Jet14Format extends Jet12Format {
@@ -1031,6 +1057,11 @@ public abstract class JetFormat {
     protected Map<String,Database.FileFormat> getPossibleFileFormats() {
       return PossibleFileFormats.POSSIBLE_VERSION_14;
     }
+
+    @Override
+    public boolean isSupportedCalculatedDataType(DataType type) {
+      return V14_CALC_TYPES.contains(type);
+    }
   }
 
 }