KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > persist > impl > Evolver


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: Evolver.java,v 1.7 2006/12/04 18:47:42 cwl Exp $
7  */

8
9 package com.sleepycat.persist.impl;
10
11 import java.util.ArrayList JavaDoc;
12 import java.util.List JavaDoc;
13 import java.util.HashMap JavaDoc;
14 import java.util.HashSet JavaDoc;
15 import java.util.IdentityHashMap JavaDoc;
16 import java.util.Map JavaDoc;
17 import java.util.Set JavaDoc;
18
19 import com.sleepycat.je.DatabaseException;
20 import com.sleepycat.je.DatabaseNotFoundException;
21 import com.sleepycat.je.Environment;
22 import com.sleepycat.je.Transaction;
23 import com.sleepycat.persist.evolve.Converter;
24 import com.sleepycat.persist.evolve.Deleter;
25 import com.sleepycat.persist.evolve.Mutation;
26 import com.sleepycat.persist.evolve.Mutations;
27 import com.sleepycat.persist.evolve.Renamer;
28 import com.sleepycat.persist.model.SecondaryKeyMetadata;
29
30 /**
31  * Evolves each old format that is still relavent if necessary, using Mutations
32  * to configure deleters, renamers, and converters.
33  *
34  * @author Mark Hayes
35  */

36 class Evolver {
37
38     static final int EVOLVE_NONE = 0;
39     static final int EVOLVE_NEEDED = 1;
40     static final int EVOLVE_FAILURE = 2;
41
42     private PersistCatalog catalog;
43     private String JavaDoc storePrefix;
44     private Mutations mutations;
45     private Map JavaDoc<String JavaDoc,Format> newFormats;
46     private boolean forceEvolution;
47     private boolean disallowClassChanges;
48     private boolean formatsChanged;
49     private boolean nestedFormatsChanged;
50     private StringBuilder JavaDoc errors;
51     private Set JavaDoc<String JavaDoc> deleteDbs;
52     private Map JavaDoc<String JavaDoc,String JavaDoc> renameDbs;
53     private Map JavaDoc<Format,Format> renameFormats;
54     private Map JavaDoc<Integer JavaDoc,Boolean JavaDoc> evolvedFormats;
55     private List JavaDoc<Format> unprocessedFormats;
56     private Map JavaDoc<Format,Set JavaDoc<Format>> subclassMap;
57
58     Evolver(PersistCatalog catalog,
59             String JavaDoc storePrefix,
60             Mutations mutations,
61             Map JavaDoc<String JavaDoc,Format> newFormats,
62             boolean forceEvolution,
63             boolean disallowClassChanges) {
64         this.catalog = catalog;
65         this.storePrefix = storePrefix;
66         this.mutations = mutations;
67         this.newFormats = newFormats;
68         this.forceEvolution = forceEvolution;
69         this.disallowClassChanges = disallowClassChanges;
70         errors = new StringBuilder JavaDoc();
71         deleteDbs = new HashSet JavaDoc<String JavaDoc>();
72         renameDbs = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
73         renameFormats = new IdentityHashMap JavaDoc<Format,Format>();
74         evolvedFormats = new HashMap JavaDoc<Integer JavaDoc,Boolean JavaDoc>();
75         unprocessedFormats = new ArrayList JavaDoc<Format>();
76         subclassMap = catalog.getSubclassMap();
77     }
78
79     final Mutations getMutations() {
80         return mutations;
81     }
82
83     /**
84      * Returns whether any formats were changed during evolution, and therefore
85      * need to be stored in the catalog.
86      */

87     boolean areFormatsChanged() {
88         return formatsChanged;
89     }
90
91     private void setFormatsChanged(Format oldFormat) {
92         checkClassChangesAllowed(oldFormat);
93         formatsChanged = true;
94         nestedFormatsChanged = true;
95         /* PersistCatalog.expectNoClassChanges is true in unit tests only. */
96         if (PersistCatalog.expectNoClassChanges) {
97             throw new IllegalStateException JavaDoc("expectNoClassChanges");
98         }
99     }
100
101     private void checkClassChangesAllowed(Format oldFormat) {
102         if (disallowClassChanges) {
103             throw new IllegalStateException JavaDoc
104                 ("When performing an upgrade changes are not allowed " +
105                  "but were made to: " + oldFormat.getClassName());
106         }
107     }
108
109     /**
110      * Returns the set of formats for a specific superclass format, or null if
111      * the superclass is not a complex type or has not subclasses.
112      */

113     Set JavaDoc<Format> getSubclassFormats(Format superFormat) {
114         return subclassMap.get(superFormat);
115     }
116
117     /**
118      * Returns an error string if any mutations are invalid or missing, or
119      * returns null otherwise. If non-null is returned, the store may not be
120      * opened.
121      */

122     String JavaDoc getErrors() {
123         if (errors.length() > 0) {
124             return errors.toString();
125         } else {
126             return null;
127         }
128     }
129
130     /**
131      * Adds a newline and the given error.
132      */

133     private void addError(String JavaDoc error) {
134         if (errors.length() > 0) {
135             errors.append("\n---\n");
136         }
137         errors.append(error);
138     }
139
140     private String JavaDoc getClassVersionLabel(Format format, String JavaDoc prefix) {
141         if (format != null) {
142             return prefix +
143                    " class: " + format.getClassName() +
144                    " version: " + format.getVersion();
145         } else {
146             return "";
147         }
148     }
149
150     /**
151      * Adds a specified error when no specific mutation is involved.
152      */

153     void addEvolveError(Format oldFormat,
154                         Format newFormat,
155                         String JavaDoc scenario,
156                         String JavaDoc error) {
157         checkClassChangesAllowed(oldFormat);
158         if (scenario == null) {
159             scenario = "Error";
160         }
161         addError(scenario + " when evolving" +
162                  getClassVersionLabel(oldFormat, "") +
163                  getClassVersionLabel(newFormat, " to") +
164                  " Error: " + error);
165     }
166
167     /**
168      * Adds an error for an invalid mutation.
169      */

170     void addInvalidMutation(Format oldFormat,
171                             Format newFormat,
172                             Mutation mutation,
173                             String JavaDoc error) {
174         checkClassChangesAllowed(oldFormat);
175         addError("Invalid mutation: " + mutation +
176                  getClassVersionLabel(oldFormat, " For") +
177                  getClassVersionLabel(newFormat, " New") +
178                  " Error: " + error);
179     }
180
181     /**
182      * Adds an error for a missing mutation.
183      */

184     void addMissingMutation(Format oldFormat,
185                             Format newFormat,
186                             String JavaDoc error) {
187         checkClassChangesAllowed(oldFormat);
188         addError("Mutation is missing to evolve" +
189                  getClassVersionLabel(oldFormat, "") +
190                  getClassVersionLabel(newFormat, " to") +
191                  " Error: " + error);
192     }
193     
194     /**
195      * Called by PersistCatalog for all non-entity formats.
196      */

197     void addNonEntityFormat(Format oldFormat) {
198         unprocessedFormats.add(oldFormat);
199     }
200
201     /**
202      * Called by PersistCatalog after calling evolveFormat or
203      * addNonEntityFormat for all old formats.
204      *
205      * We do not require deletion of an unreferenced class for three
206      * reasons: 1) built-in proxy classes may not be referenced, 2) the
207      * user may wish to declare persistent classes that are not yet used.
208      */

209     void finishEvolution() {
210         /* Process unreferenced classes. */
211         for (Format oldFormat : unprocessedFormats) {
212             oldFormat.setUnused(true);
213             Integer JavaDoc oldFormatId = oldFormat.getId();
214             if (!evolvedFormats.containsKey(oldFormatId)) {
215                 evolvedFormats.put(oldFormatId, true);
216                 boolean result = evolveFormatInternal(oldFormat);
217                 evolvedFormats.put(oldFormatId, result);
218             }
219         }
220     }
221
222     /**
223      * Called by PersistCatalog for all entity formats, and by Format.evolve
224      * methods for all potentially referenced non-entity formats.
225      */

226     boolean evolveFormat(Format oldFormat) {
227         boolean result;
228         boolean trackEntityChanges =
229             oldFormat.getLatestVersion().getEntityFormat() != null;
230         boolean saveNestedFormatsChanged = nestedFormatsChanged;
231         if (trackEntityChanges) {
232             nestedFormatsChanged = false;
233         }
234         Integer JavaDoc oldFormatId = oldFormat.getId();
235         if (evolvedFormats.containsKey(oldFormatId)) {
236             result = evolvedFormats.get(oldFormatId);
237         } else {
238             evolvedFormats.put(oldFormatId, true);
239             result = evolveFormatInternal(oldFormat);
240             evolvedFormats.put(oldFormatId, result);
241         }
242         if (oldFormat.getLatestVersion().isNew()) {
243             nestedFormatsChanged = true;
244         }
245         if (trackEntityChanges) {
246             if (nestedFormatsChanged) {
247                 Format latest = oldFormat.getLatestVersion().getEntityFormat();
248                 if (latest != null) {
249                     latest.setEvolveNeeded(true);
250                 }
251             }
252             nestedFormatsChanged = saveNestedFormatsChanged;
253         }
254         return result;
255     }
256
257     /**
258      * Tries to evolve a given existing format to the current version of the
259      * class and returns false if an invalid mutation is encountered or the
260      * configured mutations are not sufficient.
261      */

262     private boolean evolveFormatInternal(Format oldFormat) {
263
264         /* Predefined formats and deleted classes never need evolving. */
265         if (Format.isPredefined(oldFormat) || oldFormat.isDeleted()) {
266             return true;
267         }
268
269         /* Get class mutations. */
270         String JavaDoc oldName = oldFormat.getClassName();
271         int oldVersion = oldFormat.getVersion();
272         Renamer renamer = mutations.getRenamer(oldName, oldVersion, null);
273         Deleter deleter = mutations.getDeleter(oldName, oldVersion, null);
274         Converter converter =
275             mutations.getConverter(oldName, oldVersion, null);
276         if (deleter != null && (converter != null || renamer != null)) {
277             addInvalidMutation
278                 (oldFormat, null, deleter,
279                  "Class Deleter not allowed along with a Renamer or " +
280                  "Converter for the same class");
281             return false;
282         }
283
284         /*
285          * For determining the new name, arrays get special treatment. The
286          * component format is evolved in the process, and we disallow muations
287          * for arrays.
288          */

289         String JavaDoc newName;
290         if (oldFormat.isArray()) {
291             if (deleter != null || converter != null || renamer != null) {
292                 Mutation mutation = (deleter != null) ? deleter :
293                     ((converter != null) ? converter : renamer);
294                 addInvalidMutation
295                     (oldFormat, null, mutation,
296                      "Mutations not allowed for an array");
297                 return false;
298             }
299             Format compFormat = oldFormat.getComponentType();
300             if (!evolveFormat(compFormat)) {
301                 return false;
302             }
303             Format latest = compFormat.getLatestVersion();
304             if (latest != compFormat) {
305                 newName = (latest.isArray() ? "[" : "[L") +
306                            latest.getClassName() + ';';
307             } else {
308                 newName = oldName;
309             }
310         } else {
311             newName = (renamer != null) ? renamer.getNewName() : oldName;
312         }
313
314         /* Try to get the new class format. Save exception for later. */
315         Format newFormat;
316         String JavaDoc newFormatException;
317         try {
318             Class JavaDoc newClass = SimpleCatalog.classForName(newName);
319             try {
320                 newFormat = catalog.createFormat(newClass, newFormats);
321                 assert newFormat != oldFormat : newFormat.getClassName();
322                 newFormatException = null;
323             } catch (Exception JavaDoc e) {
324                 newFormat = null;
325                 newFormatException = e.toString();
326             }
327         } catch (ClassNotFoundException JavaDoc e) {
328             newFormat = null;
329             newFormatException = e.toString();
330         }
331
332         if (newFormat != null) {
333
334             /*
335              * If the old format is not the existing latest version and the new
336              * format is not an existing format, then we must evolve the latest
337              * old version to the new format first. We cannot evolve old
338              * format to a new format that may be discarded because it is equal
339              * to the latest existing format (which will remain the current
340              * version).
341              */

342             if (oldFormat != oldFormat.getLatestVersion() &&
343                 newFormat.getPreviousVersion() == null) {
344                 assert newFormats.containsValue(newFormat);
345                 Format oldLatestFormat = oldFormat.getLatestVersion();
346                 evolveFormat(oldLatestFormat);
347                 if (oldLatestFormat == oldLatestFormat.getLatestVersion()) {
348                     assert !newFormats.containsValue(newFormat);
349                     /* newFormat equals oldLatestFormat and was discarded. */
350                     newFormat = oldLatestFormat;
351                 }
352             }
353
354             /*
355              * If the old format was previously evolved to the new format
356              * (which means the new format is actually an existing format),
357              * then there is nothing to do. This is the case where no class
358              * changes were made.
359              *
360              * However, if mutations were specified when opening the catalog
361              * that are different than the mutations last used, then we must
362              * force the re-evolution of all old formats.
363              */

364             if (!forceEvolution &&
365                 newFormat == oldFormat.getLatestVersion()) {
366                 return true;
367             }
368         }
369
370         /* Apply class Renamer and continue if successful. */
371         if (renamer != null) {
372             if (!applyRenamer(renamer, oldFormat, newFormat)) {
373                 return false;
374             }
375         }
376
377         /* Apply class Converter and return. */
378         if (converter != null) {
379             if (oldFormat.isEntity()) {
380                 if (newFormat == null || !newFormat.isEntity()) {
381                     addInvalidMutation
382                         (oldFormat, newFormat, deleter,
383                          "Class converter not allowed for an entity class " +
384                          "that is no longer present or not having an " +
385                          "@Entity annotation");
386                     return false;
387                 }
388                 if (!oldFormat.evolveMetadata(newFormat, converter, this)) {
389                     return false;
390                 }
391             }
392             return applyConverter(converter, oldFormat, newFormat);
393         }
394
395         /* Apply class Deleter and return. */
396         boolean needDeleter =
397             (newFormat == null) ||
398             (newFormat.isEntity() != oldFormat.isEntity());
399         if (deleter != null) {
400             if (!needDeleter) {
401                 addInvalidMutation
402                     (oldFormat, newFormat, deleter,
403                      "Class deleter not allowed when the class and its " +
404                      "@Entity or @Persistent annotation is still present");
405                 return false;
406             }
407             return applyDeleter(deleter, oldFormat, newFormat);
408         } else {
409             if (needDeleter) {
410                 if (newFormat == null) {
411                     assert newFormatException != null;
412             /* FindBugs newFormat known to be null excluded. */
413                     addMissingMutation
414                         (oldFormat, newFormat, newFormatException);
415                 } else {
416                     addMissingMutation
417                         (oldFormat, newFormat,
418                          "@Entity switched to/from @Persistent");
419                 }
420                 return false;
421             }
422         }
423
424         /*
425          * Class-level mutations have been applied. Now apply field mutations
426          * (for complex classes) or special conversions (enum conversions, for
427          * example) by calling the old format's evolve method.
428          */

429         return oldFormat.evolve(newFormat, this);
430     }
431
432     /**
433      * Use the old format and discard the new format. Called by
434      * Format.evolve when the old and new formats are identical.
435      */

436     void useOldFormat(Format oldFormat, Format newFormat) {
437         Format renamedFormat = renameFormats.get(oldFormat);
438         if (renamedFormat != null) {
439
440             /*
441              * The format was renamed but, because this method is called, we
442              * know that no other class changes were made. Use the new/renamed
443              * format as the reader.
444              */

445             assert renamedFormat == newFormat;
446             useEvolvedFormat(oldFormat, renamedFormat, renamedFormat);
447         } else if (newFormat != null &&
448                    oldFormat.getVersion() != newFormat.getVersion()) {
449             /* The user wants a new version number, but no other changes. */
450             useEvolvedFormat(oldFormat, newFormat, newFormat);
451         } else {
452             /* The new format is discarded. */
453             catalog.useExistingFormat(oldFormat);
454             if (newFormat != null) {
455                 newFormats.remove(newFormat.getClassName());
456             }
457         }
458     }
459
460     /**
461      * Install an evolver Reader in the old format. Called by Format.evolve
462      * when the old and new formats are not identical.
463      */

464     void useEvolvedFormat(Format oldFormat,
465                           Reader evolveReader,
466                           Format newFormat) {
467         oldFormat.setReader(evolveReader);
468         if (newFormat != null) {
469             oldFormat.setLatestVersion(newFormat);
470         }
471         setFormatsChanged(oldFormat);
472     }
473     
474     private boolean applyRenamer(Renamer renamer,
475                                  Format oldFormat,
476                                  Format newFormat) {
477         if (!checkUpdatedVersion(renamer, oldFormat, newFormat)) {
478             return false;
479         }
480         if (oldFormat.isEntity() && oldFormat.isCurrentVersion()) {
481             String JavaDoc newClassName = newFormat.getClassName();
482             String JavaDoc oldClassName = oldFormat.getClassName();
483             /* Queue the renaming of the primary and secondary databases. */
484             renameDbs.put
485                 (Store.makePriDbName(storePrefix, oldClassName),
486                  Store.makePriDbName(storePrefix, newClassName));
487             for (SecondaryKeyMetadata keyMeta :
488                  oldFormat.getEntityMetadata().getSecondaryKeys().values()) {
489                 String JavaDoc keyName = keyMeta.getKeyName();
490                 renameDbs.put
491                     (Store.makeSecDbName(storePrefix, oldClassName, keyName),
492                      Store.makeSecDbName(storePrefix, newClassName, keyName));
493             }
494         }
495
496         /*
497          * Link the old format to the renamed format so that we can detect the
498          * rename in useOldFormat.
499          */

500         renameFormats.put(oldFormat, newFormat);
501
502         setFormatsChanged(oldFormat);
503         return true;
504     }
505
506     /**
507      * Called by ComplexFormat when a secondary key name is changed.
508      */

509     void renameSecondaryDatabase(Format oldFormat,
510                                  Format newFormat,
511                                  String JavaDoc oldKeyName,
512                                  String JavaDoc newKeyName) {
513         renameDbs.put
514             (Store.makeSecDbName
515                 (storePrefix, oldFormat.getClassName(), oldKeyName),
516              Store.makeSecDbName
517                 (storePrefix, newFormat.getClassName(), newKeyName));
518     }
519     
520     private boolean applyDeleter(Deleter deleter,
521                                  Format oldFormat,
522                                  Format newFormat) {
523         if (!checkUpdatedVersion(deleter, oldFormat, newFormat)) {
524             return false;
525         }
526         if (oldFormat.isEntity() && oldFormat.isCurrentVersion()) {
527             /* Queue the deletion of the primary and secondary databases. */
528             String JavaDoc className = oldFormat.getClassName();
529             deleteDbs.add(Store.makePriDbName(storePrefix, className));
530             for (SecondaryKeyMetadata keyMeta :
531                  oldFormat.getEntityMetadata().getSecondaryKeys().values()) {
532                 deleteDbs.add(Store.makeSecDbName
533                     (storePrefix, className, keyMeta.getKeyName()));
534             }
535         }
536
537         /*
538          * Set the format to deleted last, so that the above test using
539          * isCurrentVersion works properly.
540          */

541         oldFormat.setDeleted(true);
542         if (newFormat != null) {
543             oldFormat.setLatestVersion(newFormat);
544         }
545
546         setFormatsChanged(oldFormat);
547         return true;
548     }
549
550     /**
551      * Called by ComplexFormat when a secondary key is dropped.
552      */

553     void deleteSecondaryDatabase(Format oldFormat, String JavaDoc keyName) {
554         deleteDbs.add(Store.makeSecDbName
555             (storePrefix, oldFormat.getClassName(), keyName));
556     }
557     
558     private boolean applyConverter(Converter converter,
559                                    Format oldFormat,
560                                    Format newFormat) {
561         if (!checkUpdatedVersion(converter, oldFormat, newFormat)) {
562             return false;
563         }
564         Reader reader = new ConverterReader(converter);
565         useEvolvedFormat(oldFormat, reader, newFormat);
566         return true;
567     }
568
569     boolean isClassConverted(Format format) {
570         return format.getReader() instanceof ConverterReader;
571     }
572
573     private boolean checkUpdatedVersion(Mutation mutation,
574                                         Format oldFormat,
575                                         Format newFormat) {
576         if (newFormat != null &&
577             !oldFormat.isEnum() &&
578             newFormat.getVersion() <= oldFormat.getVersion()) {
579             addInvalidMutation
580                 (oldFormat, newFormat, mutation,
581                  "A new higher version number must be assigned");
582             return false;
583         } else {
584             return true;
585         }
586     }
587
588     boolean checkUpdatedVersion(String JavaDoc scenario,
589                                 Format oldFormat,
590                                 Format newFormat) {
591         if (newFormat != null &&
592             !oldFormat.isEnum() &&
593             newFormat.getVersion() <= oldFormat.getVersion()) {
594             addEvolveError
595                 (oldFormat, newFormat, scenario,
596                  "A new higher version number must be assigned");
597             return false;
598         } else {
599             return true;
600         }
601     }
602     
603     void renameAndRemoveDatabases(Environment env, Transaction txn)
604         throws DatabaseException {
605
606         for (String JavaDoc dbName : deleteDbs) {
607             try {
608                 env.removeDatabase(txn, dbName);
609             } catch (DatabaseNotFoundException ignored) {
610             }
611         }
612         for (Map.Entry JavaDoc<String JavaDoc,String JavaDoc> entry : renameDbs.entrySet()) {
613             try {
614                 env.renameDatabase(txn, entry.getKey(), entry.getValue());
615             } catch (DatabaseNotFoundException ignored) {
616             }
617         }
618     }
619
620     /**
621      * Evolves a primary key field or composite key field.
622      */

623     int evolveRequiredKeyField(Format oldParent,
624                                Format newParent,
625                                FieldInfo oldField,
626                                FieldInfo newField) {
627         int result = EVOLVE_NONE;
628         String JavaDoc oldName = oldField.getName();
629         final String JavaDoc FIELD_KIND =
630             "primary key field or composite key class field";
631         final String JavaDoc FIELD_LABEL =
632             FIELD_KIND + ": " + oldName;
633
634         if (newField == null) {
635             addMissingMutation
636                 (oldParent, newParent,
637                  "Field is missing and deletion is not allowed for a " +
638                  FIELD_LABEL);
639             return EVOLVE_FAILURE;
640         }
641
642         /* Check field mutations. Only a Renamer is allowed. */
643         Deleter deleter = mutations.getDeleter
644             (oldParent.getClassName(), oldParent.getVersion(), oldName);
645         if (deleter != null) {
646             addInvalidMutation
647                 (oldParent, newParent, deleter,
648                  "Deleter is not allowed for a " + FIELD_LABEL);
649             return EVOLVE_FAILURE;
650         }
651         Converter converter = mutations.getConverter
652             (oldParent.getClassName(), oldParent.getVersion(), oldName);
653         if (converter != null) {
654             addInvalidMutation
655                 (oldParent, newParent, converter,
656                  "Converter is not allowed for a " + FIELD_LABEL);
657             return EVOLVE_FAILURE;
658         }
659         Renamer renamer = mutations.getRenamer
660             (oldParent.getClassName(), oldParent.getVersion(), oldName);
661         String JavaDoc newName = newField.getName();
662         if (renamer != null) {
663             if (!renamer.getNewName().equals(newName)) {
664                 addInvalidMutation
665                     (oldParent, newParent, converter,
666                      "Converter is not allowed for a " + FIELD_LABEL);
667                 return EVOLVE_FAILURE;
668             }
669             result = EVOLVE_NEEDED;
670         } else {
671             if (!oldName.equals(newName)) {
672                 addMissingMutation
673                     (oldParent, newParent,
674                      "Renamer is required when field name is changed from: " +
675                      oldName + " to: " + newName);
676                 return EVOLVE_FAILURE;
677             }
678         }
679
680         /*
681          * Evolve the declared version of the field format.
682          */

683         Format oldFieldFormat = oldField.getType();
684         if (!evolveFormat(oldFieldFormat)) {
685             return EVOLVE_FAILURE;
686         }
687         Format oldLatestFormat = oldFieldFormat.getLatestVersion();
688         Format newFieldFormat = newField.getType();
689
690         if (oldLatestFormat.getClassName().equals
691                 (newFieldFormat.getClassName())) {
692             /* Formats are identical. */
693             return result;
694         } else if ((oldLatestFormat.getWrapperFormat() != null &&
695                     oldLatestFormat.getWrapperFormat().getId() ==
696                     newFieldFormat.getId()) ||
697                    (newFieldFormat.getWrapperFormat() != null &&
698                     newFieldFormat.getWrapperFormat().getId() ==
699                     oldLatestFormat.getId())) {
700             /* Primitive <-> primitive wrapper type change. */
701             return EVOLVE_NEEDED;
702         } else {
703             /* Type was changed incompatibly. */
704             addEvolveError
705                 (oldParent, newParent,
706                  "Type may not be changed for a " + FIELD_KIND,
707                  "Old field type: " + oldLatestFormat.getClassName() +
708                  " is not compatible with the new type: " +
709                  newFieldFormat.getClassName() +
710                  " for field: " + oldName);
711             return EVOLVE_FAILURE;
712         }
713     }
714 }
715
Popular Tags