1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: CollectionProxy.java,v 1.1 2008/02/07 17:12:27 mark Exp $
7 */
8
9package com.sleepycat.persist.impl;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.Set;
17import java.util.TreeSet;
18
19import com.sleepycat.bind.tuple.TupleBase;
20import com.sleepycat.db.DatabaseEntry;
21import com.sleepycat.persist.model.Persistent;
22import com.sleepycat.persist.model.PersistentProxy;
23import com.sleepycat.persist.raw.RawObject;
24
25/**
26 * Proxy for Collection types.
27 *
28 * @author Mark Hayes
29 */
30@Persistent
31abstract class CollectionProxy<E>
32    implements PersistentProxy<Collection<E>> {
33
34    private E[] elements;
35
36    protected CollectionProxy() {}
37
38    public final void initializeProxy(Collection<E> collection) {
39        elements = (E[]) new Object[collection.size()];
40        int i = 0;
41        for (E element : collection) {
42            elements[i] = element;
43            i += 1;
44        }
45    }
46
47    public final Collection<E> convertProxy() {
48        Collection<E> collection = newInstance(elements.length);
49        for (E element : elements) {
50            collection.add(element);
51        }
52        return collection;
53    }
54
55    protected abstract Collection<E> newInstance(int size);
56
57    @Persistent(proxyFor=ArrayList.class)
58    static class ArrayListProxy<E> extends CollectionProxy<E> {
59
60        protected ArrayListProxy() {}
61
62        protected Collection<E> newInstance(int size) {
63            return new ArrayList<E>(size);
64        }
65    }
66
67    @Persistent(proxyFor=LinkedList.class)
68    static class LinkedListProxy<E> extends CollectionProxy<E> {
69
70        protected LinkedListProxy() {}
71
72        protected Collection<E> newInstance(int size) {
73            return new LinkedList<E>();
74        }
75    }
76
77    @Persistent(proxyFor=HashSet.class)
78    static class HashSetProxy<E> extends CollectionProxy<E> {
79
80        protected HashSetProxy() {}
81
82        protected Collection<E> newInstance(int size) {
83            return new HashSet<E>(size);
84        }
85    }
86
87    @Persistent(proxyFor=TreeSet.class)
88    static class TreeSetProxy<E> extends CollectionProxy<E> {
89
90        protected TreeSetProxy() {}
91
92        protected Collection<E> newInstance(int size) {
93            return new TreeSet<E>();
94        }
95    }
96
97    static Object[] getElements(RawObject collection) {
98        Object value = null;
99        while (value == null && collection != null) {
100            Map<String,Object> values = collection.getValues();
101            if (values != null) {
102                value = values.get("elements");
103                if (value == null) {
104                    collection = collection.getSuper();
105                }
106            }
107        }
108        if (value == null || !(value instanceof RawObject)) {
109            throw new IllegalStateException
110                ("Collection proxy for a secondary key field must " +
111                 "contain a field named 'elements'");
112        }
113        RawObject rawObj = (RawObject) value;
114        Format format = (Format) rawObj.getType();
115        if (!format.isArray() ||
116            format.getComponentType().getId() != Format.ID_OBJECT) {
117            throw new IllegalStateException
118                ("Collection proxy 'elements' field must by an Object array");
119        }
120        return rawObj.getElements();
121    }
122
123    static void setElements(RawObject collection, Object[] elements) {
124        RawObject value = null;
125        while (value == null && collection != null) {
126            Map<String,Object> values = collection.getValues();
127            if (values != null) {
128                value = (RawObject) values.get("elements");
129                if (value != null) {
130                    values.put("elements",
131                               new RawObject(value.getType(), elements));
132                } else {
133                    collection = collection.getSuper();
134                }
135            }
136        }
137        if (value == null) {
138            throw new IllegalStateException();
139        }
140    }
141
142    static void copyElements(RecordInput input,
143                             Format format,
144                             Format keyFormat,
145                             Set results) {
146        /*
147         * This could be optimized by traversing the byte format of the
148         * collection's elements array.
149         */
150        RawObject collection = (RawObject) format.newInstance(input, true);
151        collection = (RawObject) format.readObject(collection, input, true);
152        Object[] elements = getElements(collection);
153        if (elements != null) {
154            for (Object elem : elements) {
155                RecordOutput output =
156                    new RecordOutput(input.getCatalog(), true);
157                output.writeKeyObject(elem, keyFormat);
158                DatabaseEntry entry = new DatabaseEntry();
159                TupleBase.outputToEntry(output, entry);
160                results.add(entry);
161            }
162        }
163    }
164}
165