• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/db-4.8.30/java/src/com/sleepycat/persist/evolve/
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002-2009 Oracle.  All rights reserved.
5 *
6 * $Id$
7 */
8
9package com.sleepycat.persist.evolve;
10
11import java.io.Serializable;
12
13import com.sleepycat.persist.model.EntityModel;
14import com.sleepycat.persist.raw.RawObject;
15import com.sleepycat.persist.raw.RawType;
16
17/**
18 * Converts an old version of an object value to conform to the current class
19 * or field definition.
20 *
21 * <p>The {@code Conversion} interface is implemented by the user.  A
22 * {@code Conversion} instance is passed to the {@link Converter#Converter}
23 * constructor.</p>
24 *
25 * <p>The {@code Conversion} interface extends {@link Serializable} and the
26 * {@code Conversion} instance is serialized for storage using standard Java
27 * serialization.  Normally, the {@code Conversion} class should only have
28 * transient fields that are initialized in the {@link #initialize} method.
29 * While non-transient fields are allowed, care must be taken to only include
30 * fields that are serializable and will not pull in large amounts of data.</p>
31 *
32 * <p>When a class conversion is specified, two special considerations
33 * apply:</p>
34 * <ol>
35 * <li>A class conversion is only applied when to instances of that class.  The
36 * conversion will not be applied when the class when it appears as a
37 * superclass of the instance's class.  In this case, a conversion for the
38 * instance's class must also be specified.</li>
39 * <li>Although field renaming (as well as all other changes) is handled by the
40 * conversion method, a field Renamer is still needed when a secondary key
41 * field is renamed and field Deleter is still needed when a secondary key
42 * field is deleted.  This is necessary for evolution of the metadata;
43 * specifically, if the key name changes the database must be renamed and if
44 * the key field is deleted the secondary database must be deleted.</li>
45 * </ol>
46 *
47 * <p>The {@code Conversion} class must implement the standard equals method.
48 * See {@link #equals} for more information.</p>
49 *
50 * <p>Conversions of simple types are generally simple.  For example, a {@code
51 * String} field that contains only integer values can be easily converted to
52 * an {@code int} field:</p>
53 * <pre class="code">
54 *  // The old class.  Version 0 is implied.
55 *  //
56 *  {@literal @Persistent}
57 *  class Address {
58 *      String zipCode;
59 *      ...
60 *  }
61 *
62 *  // The new class.  A new version number must be assigned.
63 *  //
64 *  {@literal @Persistent(version=1)}
65 *  class Address {
66 *      int zipCode;
67 *      ...
68 *  }
69 *
70 *  // The conversion class.
71 *  //
72 *  class MyConversion1 implements Conversion {
73 *
74 *      public void initialize(EntityModel model) {
75 *          // No initialization needed.
76 *      }
77 *
78 *      public Object convert(Object fromValue) {
79 *          return Integer.valueOf((String) fromValue);
80 *      }
81 *
82 *      {@code @Override}
83 *      public boolean equals(Object o) {
84 *          return o instanceof MyConversion1;
85 *      }
86 *  }
87 *
88 *  // Create a field converter mutation.
89 *  //
90 *  Converter converter = new Converter(Address.class.getName(), 0,
91 *                                      "zipCode", new MyConversion1());
92 *
93 *  // Configure the converter as described {@link Mutations here}.</pre>
94 *
95 * <p>A conversion may perform arbitrary transformations on an object.  For
96 * example, a conversion may transform a single String address field into an
97 * Address object containing four fields for street, city, state and zip
98 * code.</p>
99 * <pre class="code">
100 *  // The old class.  Version 0 is implied.
101 *  //
102 *  {@literal @Entity}
103 *  class Person {
104 *      String address;
105 *      ...
106 *  }
107 *
108 *  // The new class.  A new version number must be assigned.
109 *  //
110 *  {@literal @Entity(version=1)}
111 *  class Person {
112 *      Address address;
113 *      ...
114 *  }
115 *
116 *  // The new address class.
117 *  //
118 *  {@literal @Persistent}
119 *  class Address {
120 *      String street;
121 *      String city;
122 *      String state;
123 *      int zipCode;
124 *      ...
125 *  }
126 *
127 *  class MyConversion2 implements Conversion {
128 *      private transient RawType addressType;
129 *
130 *      public void initialize(EntityModel model) {
131 *          addressType = model.getRawType(Address.class.getName());
132 *      }
133 *
134 *      public Object convert(Object fromValue) {
135 *
136 *          // Parse the old address and populate the new address fields
137 *          //
138 *          String oldAddress = (String) fromValue;
139 *          {@literal Map<String,Object> addressValues = new HashMap<String,Object>();}
140 *          addressValues.put("street", parseStreet(oldAddress));
141 *          addressValues.put("city", parseCity(oldAddress));
142 *          addressValues.put("state", parseState(oldAddress));
143 *          addressValues.put("zipCode", parseZipCode(oldAddress));
144 *
145 *          // Return new raw Address object
146 *          //
147 *          return new RawObject(addressType, addressValues, null);
148 *      }
149 *
150 *      {@code @Override}
151 *      public boolean equals(Object o) {
152 *          return o instanceof MyConversion2;
153 *      }
154 *
155 *      private String parseStreet(String oldAddress) { ... }
156 *      private String parseCity(String oldAddress) { ... }
157 *      private String parseState(String oldAddress) { ... }
158 *      private Integer parseZipCode(String oldAddress) { ... }
159 *  }
160 *
161 *  // Create a field converter mutation.
162 *  //
163 *  Converter converter = new Converter(Person.class.getName(), 0,
164 *                                      "address", new MyConversion2());
165 *
166 *  // Configure the converter as described {@link Mutations here}.</pre>
167 *
168 * <p>Note that when a conversion returns a {@link RawObject}, it must return
169 * it with a {@link RawType} that is current as defined by the current class
170 * definitions.  The proper types can be obtained from the {@link EntityModel}
171 * in the conversion's {@link #initialize initialize} method.</p>
172 *
173 * <p>A variation on the example above is where several fields in a class
174 * (street, city, state and zipCode) are converted to a single field (address).
175 * In this case a class converter rather than a field converter is used.</p>
176 *
177 * <pre class="code">
178 *  // The old class.  Version 0 is implied.
179 *  //
180 *  {@literal @Entity}
181 *  class Person {
182 *      String street;
183 *      String city;
184 *      String state;
185 *      int zipCode;
186 *      ...
187 *  }
188 *
189 *  // The new class.  A new version number must be assigned.
190 *  //
191 *  {@literal @Entity(version=1)}
192 *  class Person {
193 *      Address address;
194 *      ...
195 *  }
196 *
197 *  // The new address class.
198 *  //
199 *  {@literal @Persistent}
200 *  class Address {
201 *      String street;
202 *      String city;
203 *      String state;
204 *      int zipCode;
205 *      ...
206 *  }
207 *
208 *  class MyConversion3 implements Conversion {
209 *      private transient RawType newPersonType;
210 *      private transient RawType addressType;
211 *
212 *      public void initialize(EntityModel model) {
213 *          newPersonType = model.getRawType(Person.class.getName());
214 *          addressType = model.getRawType(Address.class.getName());
215 *      }
216 *
217 *      public Object convert(Object fromValue) {
218 *
219 *          // Get field value maps for old and new objects.
220 *          //
221 *          RawObject person = (RawObject) fromValue;
222 *          {@literal Map<String,Object> personValues = person.getValues();}
223 *          {@literal Map<String,Object> addressValues = new HashMap<String,Object>();}
224 *          RawObject address = new RawObject(addressType, addressValues, null);
225 *
226 *          // Remove the old address fields and insert the new one.
227 *          //
228 *          addressValues.put("street", personValues.remove("street"));
229 *          addressValues.put("city", personValues.remove("city"));
230 *          addressValues.put("state", personValues.remove("state"));
231 *          addressValues.put("zipCode", personValues.remove("zipCode"));
232 *          personValues.put("address", address);
233 *
234 *          return new RawObject(newPersonType, personValues, person.getSuper());
235 *      }
236 *
237 *      {@code @Override}
238 *      public boolean equals(Object o) {
239 *          return o instanceof MyConversion3;
240 *      }
241 *  }
242 *
243 *  // Create a class converter mutation.
244 *  //
245 *  Converter converter = new Converter(Person.class.getName(), 0,
246 *                                      new MyConversion3());
247 *
248 *  // Configure the converter as described {@link Mutations here}.</pre>
249 *
250 *
251 * <p>A conversion can also handle changes to class hierarchies.  For example,
252 * if a "name" field originally declared in class A is moved to its superclass
253 * B, a conversion can move the field value accordingly:</p>
254 *
255 * <pre class="code">
256 *  // The old classes.  Version 0 is implied.
257 *  //
258 *  {@literal @Persistent}
259 *  class A extends B {
260 *      String name;
261 *      ...
262 *  }
263 *  {@literal @Persistent}
264 *  abstract class B {
265 *      ...
266 *  }
267 *
268 *  // The new classes.  A new version number must be assigned.
269 *  //
270 *  {@literal @Persistent(version=1)}
271 *  class A extends B {
272 *      ...
273 *  }
274 *  {@literal @Persistent(version=1)}
275 *  abstract class B {
276 *      String name;
277 *      ...
278 *  }
279 *
280 *  class MyConversion4 implements Conversion {
281 *      private transient RawType newAType;
282 *      private transient RawType newBType;
283 *
284 *      public void initialize(EntityModel model) {
285 *          newAType = model.getRawType(A.class.getName());
286 *          newBType = model.getRawType(B.class.getName());
287 *      }
288 *
289 *      public Object convert(Object fromValue) {
290 *          RawObject oldA = (RawObject) fromValue;
291 *          RawObject oldB = oldA.getSuper();
292 *          {@literal Map<String,Object> aValues = oldA.getValues();}
293 *          {@literal Map<String,Object> bValues = oldB.getValues();}
294 *          bValues.put("name", aValues.remove("name"));
295 *          RawObject newB = new RawObject(newBType, bValues, oldB.getSuper());
296 *          RawObject newA = new RawObject(newAType, aValues, newB);
297 *          return newA;
298 *      }
299 *
300 *      {@code @Override}
301 *      public boolean equals(Object o) {
302 *          return o instanceof MyConversion4;
303 *      }
304 *  }
305 *
306 *  // Create a class converter mutation.
307 *  //
308 *  Converter converter = new Converter(A.class.getName(), 0,
309 *                                      new MyConversion4());
310 *
311 *  // Configure the converter as described {@link Mutations here}.</pre>
312 *
313 * <p>A conversion may return an instance of a different class entirely, as
314 * long as it conforms to current class definitions and is the type expected
315 * in the given context (a subtype of the old type, or a type compatible with
316 * the new field type).  For example, a field that is used to discriminate
317 * between two types of objects could be removed and replaced by two new
318 * subclasses:</p> <pre class="code">
319 *  // The old class.  Version 0 is implied.
320 *  //
321 *  {@literal @Persistent}
322 *  class Pet {
323 *      boolean isCatNotDog;
324 *      ...
325 *  }
326 *
327 *  // The new classes.  A new version number must be assigned to the Pet class.
328 *  //
329 *  {@literal @Persistent(version=1)}
330 *  class Pet {
331 *      ...
332 *  }
333 *  {@literal @Persistent}
334 *  class Cat extends Pet {
335 *      ...
336 *  }
337 *  {@literal @Persistent}
338 *  class Dog extends Pet {
339 *      ...
340 *  }
341 *
342 *  class MyConversion5 implements Conversion {
343 *      private transient RawType newPetType;
344 *      private transient RawType dogType;
345 *      private transient RawType catType;
346 *
347 *      public void initialize(EntityModel model) {
348 *          newPetType = model.getRawType(Pet.class.getName());
349 *          dogType = model.getRawType(Dog.class.getName());
350 *          catType = model.getRawType(Cat.class.getName());
351 *      }
352 *
353 *      public Object convert(Object fromValue) {
354 *          RawObject pet = (RawObject) fromValue;
355 *          {@literal Map<String,Object> petValues = pet.getValues();}
356 *          Boolean isCat = (Boolean) petValues.remove("isCatNotDog");
357 *          RawObject newPet = new RawObject(newPetType, petValues,
358 *                                           pet.getSuper());
359 *          RawType newSubType = isCat ? catType : dogType;
360 *          return new RawObject(newSubType, Collections.emptyMap(), newPet);
361 *      }
362 *
363 *      {@code @Override}
364 *      public boolean equals(Object o) {
365 *          return o instanceof MyConversion5;
366 *      }
367 *  }
368 *
369 *  // Create a class converter mutation.
370 *  //
371 *  Converter converter = new Converter(Pet.class.getName(), 0,
372 *                                      new MyConversion5());
373 *
374 *  // Configure the converter as described {@link Mutations here}.</pre>
375 *
376 * <p>The primary limitation of a conversion is that it may access at most a
377 * single entity instance at one time.  Conversions involving multiple entities
378 * at once may be made by performing a <a
379 * href="package-summary.html#storeConversion">store conversion</a>.</p>
380 *
381 * @see com.sleepycat.persist.evolve Class Evolution
382 * @author Mark Hayes
383 */
384public interface Conversion extends Serializable {
385
386    /**
387     * Initializes the conversion, allowing it to obtain raw type information
388     * from the entity model.
389     */
390    void initialize(EntityModel model);
391
392    /**
393     * Converts an old version of an object value to conform to the current
394     * class or field definition.
395     *
396     * <p>If a {@link RuntimeException} is thrown by this method, it will be
397     * thrown to the original caller.  Similarly, a {@link
398     * IllegalArgumentException} will be thrown to the original caller if the
399     * object returned by this method does not conform to current class
400     * definitions.</p>
401     *
402     * <p>The class of the input and output object may be one of the simple
403     * types or {@link RawObject}.  For primitive types, the primitive wrapper
404     * class is used.</p>
405     *
406     * @param fromValue the object value being converted.  The type of this
407     * value is defined by the old class version that is being converted.
408     *
409     * @return the converted object.  The type of this value must conform to
410     * a current class definition.  If this is a class conversion, it must
411     * be the current version of the class.  If this is a field conversion, it
412     * must be of a type compatible with the current declared type of the
413     * field.
414     */
415    Object convert(Object fromValue);
416
417    /**
418     * The standard {@code equals} method that must be implemented by
419     * conversion class.
420     *
421     * <p>When mutations are specified when opening a store, the specified and
422     * previously stored mutations are compared for equality.  If they are
423     * equal, there is no need to replace the existing mutations in the stored
424     * catalog.  To accurately determine equality, the conversion class must
425     * implement the {@code equals} method.</p>
426     *
427     * <p>If the {@code equals} method is not explicitly implemented by the
428     * conversion class or a superclass other than {@code Object}, {@code
429     * IllegalArgumentException} will be thrown when the store is opened.</p>
430     *
431     * <p>Normally whenever {@code equals} is implemented the {@code hashCode}
432     * method should also be implemented to support hash sets and maps.
433     * However, hash sets and maps containing <code>Conversion</code> objects
434     * are not used by the DPL and therefore the DPL does not require
435     * {@code hashCode} to be implemented.</p>
436     */
437    boolean equals(Object other);
438}
439