View Javadoc
1   /*
2   Copyright (c) 2011 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.io.IOException;
20  import java.time.LocalDateTime;
21  import java.util.Date;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.Map;
26  
27  import com.healthmarketscience.jackcess.DataType;
28  import com.healthmarketscience.jackcess.PropertyMap;
29  
30  /**
31   * Map of properties for a database object.
32   *
33   * @author James Ahlborn
34   */
35  public class PropertyMapImpl implements PropertyMap
36  {
37    private static final Map<String,PropDef> DEFAULT_TYPES =
38      new HashMap<String,PropDef>();
39  
40    static {
41      DEFAULT_TYPES.put(ACCESS_VERSION_PROP, new PropDef(DataType.TEXT, false));
42      DEFAULT_TYPES.put(TITLE_PROP, new PropDef(DataType.TEXT, false));
43      DEFAULT_TYPES.put(AUTHOR_PROP, new PropDef(DataType.TEXT, false));
44      DEFAULT_TYPES.put(COMPANY_PROP, new PropDef(DataType.TEXT, false));
45  
46      DEFAULT_TYPES.put(DEFAULT_VALUE_PROP, new PropDef(DataType.MEMO, true));
47      DEFAULT_TYPES.put(REQUIRED_PROP, new PropDef(DataType.BOOLEAN, true));
48      DEFAULT_TYPES.put(ALLOW_ZERO_LEN_PROP, new PropDef(DataType.BOOLEAN, true));
49      DEFAULT_TYPES.put(DECIMAL_PLACES_PROP, new PropDef(DataType.BYTE, true));
50      DEFAULT_TYPES.put(FORMAT_PROP, new PropDef(DataType.TEXT, true));
51      DEFAULT_TYPES.put(INPUT_MASK_PROP, new PropDef(DataType.TEXT, true));
52      DEFAULT_TYPES.put(CAPTION_PROP, new PropDef(DataType.MEMO, false));
53      DEFAULT_TYPES.put(VALIDATION_RULE_PROP, new PropDef(DataType.TEXT, true));
54      DEFAULT_TYPES.put(VALIDATION_TEXT_PROP, new PropDef(DataType.TEXT, true));
55      DEFAULT_TYPES.put(GUID_PROP, new PropDef(DataType.BINARY, true));
56      DEFAULT_TYPES.put(DESCRIPTION_PROP, new PropDef(DataType.MEMO, false));
57      DEFAULT_TYPES.put(RESULT_TYPE_PROP, new PropDef(DataType.BYTE, true));
58      DEFAULT_TYPES.put(EXPRESSION_PROP, new PropDef(DataType.MEMO, true));
59      DEFAULT_TYPES.put(DISPLAY_CONTROL_PROP, new PropDef(DataType.INT, false));
60      DEFAULT_TYPES.put(TEXT_FORMAT_PROP, new PropDef(DataType.BYTE, false));
61      DEFAULT_TYPES.put(IME_MODE_PROP, new PropDef(DataType.BYTE, false));
62      DEFAULT_TYPES.put(IME_SENTENCE_MODE_PROP, new PropDef(DataType.BYTE, false));
63    }
64  
65    private final String _mapName;
66    private final short _mapType;
67    private final Map<String,Property> _props =
68      new LinkedHashMap<String,Property>();
69    private final PropertyMaps _owner;
70  
71    public PropertyMapImpl(String name, short type, PropertyMaps owner) {
72      _mapName = name;
73      _mapType = type;
74      _owner = owner;
75    }
76  
77    @Override
78    public String getName() {
79      return _mapName;
80    }
81  
82    public short getType() {
83      return _mapType;
84    }
85  
86    public PropertyMaps getOwner() {
87      return _owner;
88    }
89  
90    @Override
91    public int getSize() {
92      return _props.size();
93    }
94  
95    @Override
96    public boolean isEmpty() {
97      return _props.isEmpty();
98    }
99  
100   @Override
101   public Property get(String name) {
102     return _props.get(DatabaseImpl.toLookupName(name));
103   }
104 
105   @Override
106   public Object getValue(String name) {
107     return getValue(name, null);
108   }
109 
110   @Override
111   public Object getValue(String name, Object defaultValue) {
112     Property prop = get(name);
113     Object value = defaultValue;
114     if((prop != null) && (prop.getValue() != null)) {
115       value = prop.getValue();
116     }
117     return value;
118   }
119 
120   @Override
121   public PropertyImpl put(String name, Object value) {
122     return put(name, null, value, false);
123   }
124 
125   @Override
126   public PropertyImpl put(String name, DataType type, Object value) {
127     return put(name, type, value, false);
128   }
129 
130   @Override
131   public void putAll(Iterable<? extends Property> props) {
132     if(props == null) {
133       return;
134     }
135 
136     for(Property prop : props) {
137       put(prop);
138     }
139   }
140 
141   public PropertyImpl put(Property prop) {
142     return put(prop.getName(), prop.getType(), prop.getValue(), prop.isDdl());
143   }
144 
145   /**
146    * Puts a property into this map with the given information.
147    */
148   @Override
149   public PropertyImpl put(String name, DataType type, Object value,
150                           boolean isDdl) {
151     PropertyImpl prop = (PropertyImpl)createProperty(name, type, value, isDdl);
152     _props.put(DatabaseImpl.toLookupName(name), prop);
153     return prop;
154   }
155 
156   @Override
157   public PropertyImpl remove(String name) {
158     return (PropertyImpl)_props.remove(DatabaseImpl.toLookupName(name));
159   }
160 
161   @Override
162   public Iterator<Property> iterator() {
163     return _props.values().iterator();
164   }
165 
166   @Override
167   public void save() throws IOException {
168     getOwner().save();
169   }
170 
171   @Override
172   public String toString() {
173     return toString(this);
174   }
175 
176   public static String toString(PropertyMap map) {
177     StringBuilder sb = new StringBuilder();
178     sb.append(PropertyMaps.DEFAULT_NAME.equals(map.getName()) ?
179               "<DEFAULT>" : map.getName())
180       .append(" {");
181     for(Iterator<Property> iter = map.iterator(); iter.hasNext(); ) {
182       sb.append(iter.next());
183       if(iter.hasNext()) {
184         sb.append(",");
185       }
186     }
187     sb.append("}");
188     return sb.toString();
189   }
190 
191   public static Property createProperty(String name, DataType type, Object value) {
192     return createProperty(name, type, value, false);
193   }
194 
195   public static Property createProperty(String name, DataType type,
196                                         Object value, boolean isDdl) {
197     // see if this is a builtin property that we already understand
198     PropDef pd = DEFAULT_TYPES.get(name);
199 
200     if(value instanceof PropertyMap.EnumValue) {
201       // convert custom enum to stored value
202       value = ((PropertyMap.EnumValue)value).getValue();
203     }
204 
205     if(pd != null) {
206       // update according to the default info
207       type = ((type == null) ? pd._type : type);
208       isDdl |= pd._isDdl;
209     } else if(type == null) {
210       // choose the type based on the value
211       if(value instanceof String) {
212         type = DataType.TEXT;
213       } else if(value instanceof Boolean) {
214         type = DataType.BOOLEAN;
215       } else if(value instanceof Byte) {
216         type = DataType.BYTE;
217       } else if(value instanceof Short) {
218         type = DataType.INT;
219       } else if(value instanceof Integer) {
220         type = DataType.LONG;
221       } else if(value instanceof Float) {
222         type = DataType.FLOAT;
223       } else if(value instanceof Double) {
224         type = DataType.DOUBLE;
225       } else if((value instanceof Date) || (value instanceof LocalDateTime)) {
226         type = DataType.SHORT_DATE_TIME;
227       } else if(value instanceof byte[]) {
228         type = DataType.OLE;
229       } else if(value instanceof Long) {
230         type = DataType.BIG_INT;
231       } else {
232         throw new IllegalArgumentException(
233             "Could not determine type for property " + name +
234             " with value " + value);
235       }
236     }
237 
238     return new PropertyImpl(name, type, value, isDdl);
239   }
240 
241   /**
242    * Info about a property defined in a PropertyMap.
243    */
244   static final class PropertyImpl implements PropertyMap.Property
245   {
246     private final String _name;
247     private final DataType _type;
248     private final boolean _ddl;
249     private Object _value;
250 
251     private PropertyImpl(String name, DataType type, Object value,
252                          boolean ddl) {
253       _name = name;
254       _type = type;
255       _ddl = ddl;
256       _value = value;
257     }
258 
259     @Override
260     public String getName() {
261       return _name;
262     }
263 
264     @Override
265     public DataType getType() {
266       return _type;
267     }
268 
269     @Override
270     public Object getValue() {
271       return _value;
272     }
273 
274     @Override
275     public void setValue(Object newValue) {
276       _value = newValue;
277     }
278 
279     @Override
280     public boolean isDdl() {
281       return _ddl;
282     }
283 
284     @Override
285     public String toString() {
286       Object val = getValue();
287       if(val instanceof byte[]) {
288         val = ByteUtil.toHexString((byte[])val);
289       }
290       return getName() + "[" + getType() + (_ddl ? ":ddl" : "") + "]=" + val;
291     }
292   }
293 
294   /**
295    * Helper for holding info about default properties
296    */
297   private static final class PropDef
298   {
299     private final DataType _type;
300     private final boolean _isDdl;
301 
302     private PropDef(DataType type, boolean isDdl) {
303       _type = type;
304       _isDdl = isDdl;
305     }
306   }
307 }