1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: Mutations.java,v 1.1 2008/02/07 17:12:27 mark Exp $
7 */
8
9package com.sleepycat.persist.evolve;
10
11import java.io.Serializable;
12import java.util.Collection;
13import java.util.HashMap;
14import java.util.Map;
15
16import com.sleepycat.persist.EntityStore;
17import com.sleepycat.persist.StoreConfig;
18
19/**
20 * A collection of mutations for configuring class evolution.
21 *
22 * <p>Mutations are configured when a store is opened via {@link
23 * StoreConfig#setMutations StoreConfig.setMutations}.  For example:</p>
24 *
25 * <pre class="code">
26 *  Mutations mutations = new Mutations();
27 *  // Add mutations...
28 *  StoreConfig config = new StoreConfig();
29 *  config.setMutations(mutations);
30 *  EntityStore store = new EntityStore(env, "myStore", config);</pre>
31 *
32 * <p>Mutations cause data conversion to occur lazily as instances are read
33 * from the store.  The {@link EntityStore#evolve EntityStore.evolve} method
34 * may also be used to perform eager conversion.</p>
35 *
36 * <p>Not all incompatible class changes can be handled via mutations.  For
37 * example, complex refactoring may require a transformation that manipulates
38 * multiple entity instances at once.  Such changes are not possible with
39 * mutations but can made by performing a <a
40 * href="package-summary.html#storeConversion">store conversion</a>.</p>
41 *
42 * @see com.sleepycat.persist.evolve Class Evolution
43 * @author Mark Hayes
44 */
45public class Mutations implements Serializable {
46
47    private static final long serialVersionUID = -1744401530444812916L;
48
49    private Map<Mutation,Renamer> renamers;
50    private Map<Mutation,Deleter> deleters;
51    private Map<Mutation,Converter> converters;
52
53    /**
54     * Creates an empty set of mutations.
55     */
56    public Mutations() {
57        renamers = new HashMap<Mutation,Renamer>();
58        deleters = new HashMap<Mutation,Deleter>();
59        converters = new HashMap<Mutation,Converter>();
60    }
61
62    /**
63     * Returns true if no mutations are present.
64     */
65    public boolean isEmpty() {
66        return renamers.isEmpty() &&
67               deleters.isEmpty() &&
68               converters.isEmpty();
69    }
70
71    /**
72     * Adds a renamer mutation.
73     */
74    public void addRenamer(Renamer renamer) {
75        renamers.put(new Key(renamer), renamer);
76    }
77
78    /**
79     * Returns the renamer mutation for the given class, version and field, or
80     * null if none exists.  A null field name should be specified to get a
81     * class renamer.
82     */
83    public Renamer getRenamer(String className,
84                              int classVersion,
85                              String fieldName) {
86        return renamers.get(new Key(className, classVersion, fieldName));
87    }
88
89    /**
90     * Returns an unmodifiable collection of all renamer mutations.
91     */
92    public Collection<Renamer> getRenamers() {
93        return renamers.values();
94    }
95
96    /**
97     * Adds a deleter mutation.
98     */
99    public void addDeleter(Deleter deleter) {
100        deleters.put(new Key(deleter), deleter);
101    }
102
103    /**
104     * Returns the deleter mutation for the given class, version and field, or
105     * null if none exists.  A null field name should be specified to get a
106     * class deleter.
107     */
108    public Deleter getDeleter(String className,
109                              int classVersion,
110                              String fieldName) {
111        return deleters.get(new Key(className, classVersion, fieldName));
112    }
113
114    /**
115     * Returns an unmodifiable collection of all deleter mutations.
116     */
117    public Collection<Deleter> getDeleters() {
118        return deleters.values();
119    }
120
121    /**
122     * Adds a converter mutation.
123     */
124    public void addConverter(Converter converter) {
125        converters.put(new Key(converter), converter);
126    }
127
128    /**
129     * Returns the converter mutation for the given class, version and field,
130     * or null if none exists.  A null field name should be specified to get a
131     * class converter.
132     */
133    public Converter getConverter(String className,
134                                  int classVersion,
135                                  String fieldName) {
136        return converters.get(new Key(className, classVersion, fieldName));
137    }
138
139    /**
140     * Returns an unmodifiable collection of all converter mutations.
141     */
142    public Collection<Converter> getConverters() {
143        return converters.values();
144    }
145
146    private static class Key extends Mutation {
147
148        Key(String className, int classVersion, String fieldName) {
149            super(className, classVersion, fieldName);
150        }
151
152        Key(Mutation mutation) {
153            super(mutation.getClassName(),
154                  mutation.getClassVersion(),
155                  mutation.getFieldName());
156        }
157    }
158
159    /**
160     * Returns true if this collection has the same set of mutations as the
161     * given collection and all mutations are equal.
162     */
163    @Override
164    public boolean equals(Object other) {
165        if (other instanceof Mutations) {
166            Mutations o = (Mutations) other;
167            return renamers.equals(o.renamers) &&
168                   deleters.equals(o.deleters) &&
169                   converters.equals(o.converters);
170        } else {
171            return false;
172        }
173    }
174
175    @Override
176    public int hashCode() {
177        return renamers.hashCode() +
178               deleters.hashCode() +
179               converters.hashCode();
180    }
181
182    @Override
183    public String toString() {
184        StringBuffer buf = new StringBuffer();
185        if (renamers.size() > 0) {
186            buf.append(renamers.values());
187        }
188        if (deleters.size() > 0) {
189            buf.append(deleters.values());
190        }
191        if (converters.size() > 0) {
192            buf.append(converters.values());
193        }
194        if (buf.length() > 0) {
195            return buf.toString();
196        } else {
197            return "[Empty Mutations]";
198        }
199    }
200}
201