1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: EnhancedAccessor.java,v 1.1 2008/02/07 17:12:27 mark Exp $
7 */
8
9package com.sleepycat.persist.impl;
10
11import java.lang.reflect.Array;
12import java.lang.reflect.Modifier;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.Map;
16
17/**
18 * Implements Accessor for a complex persistent class.
19 *
20 * @author Mark Hayes
21 */
22public class EnhancedAccessor implements Accessor {
23
24    private static final Map<String,Enhanced> classRegistry =
25        Collections.synchronizedMap(new HashMap<String,Enhanced>());
26
27    /* Is public for unit tests. */
28    public static final boolean EXPECT_ENHANCED =
29        "true".equals(System.getProperty("expectEnhanced"));
30
31    private Enhanced prototype;
32    private Format priKeyFormat;
33    private Class type;
34
35    /**
36     * Registers a prototype instance, and should be called during
37     * initialization of the prototype class.  The prototype may be null for
38     * an abstract class.
39     */
40    public static void registerClass(String className, Enhanced prototype) {
41        classRegistry.put(className, prototype);
42    }
43
44    /**
45     * Returns whether a given class is a (registered) enhanced class.
46     */
47    static boolean isEnhanced(Class type) {
48        boolean enhanced = classRegistry.containsKey(type.getName());
49        if (!enhanced && EXPECT_ENHANCED) {
50            throw new IllegalStateException
51                ("Test was run with expectEnhanced=true but class " +
52                 type.getName() + " is not enhanced");
53        }
54        return enhanced;
55    }
56
57    /**
58     * Creates an accessor.
59     */
60    EnhancedAccessor(Class type) {
61        this.type = type;
62        prototype = classRegistry.get(type.getName());
63        assert prototype != null || Modifier.isAbstract(type.getModifiers());
64    }
65
66    /**
67     * Creates an accessor for a complex type.
68     */
69    EnhancedAccessor(Catalog catalog, Class type, ComplexFormat format) {
70        this(type);
71
72        /*
73         * Find the primary key format for this format or one of its superclass
74         * formats.
75         */
76        ComplexFormat declaringFormat = format;
77        while (declaringFormat != null) {
78            String priKeyField = declaringFormat.getPriKeyField();
79            if (priKeyField != null) {
80                Class declaringType = declaringFormat.getType();
81                Class fieldType;
82                try {
83                    fieldType =
84                        declaringType.getDeclaredField(priKeyField).getType();
85                } catch (NoSuchFieldException e) {
86                    throw new IllegalStateException(e);
87                }
88                priKeyFormat = catalog.getFormat(fieldType);
89                break;
90            } else {
91                Format superFormat = declaringFormat.getSuperFormat();
92                declaringFormat = (ComplexFormat) superFormat;
93            }
94        }
95    }
96
97    public Object newInstance() {
98        if (prototype == null) {
99            /* Abstract class -- internal error. */
100            throw new IllegalStateException();
101        }
102        return prototype.bdbNewInstance();
103    }
104
105    public Object newArray(int len) {
106        if (prototype == null) {
107            /* Abstract class -- use reflection for now. */
108            return Array.newInstance(type, len);
109        }
110        return prototype.bdbNewArray(len);
111    }
112
113    public boolean isPriKeyFieldNullOrZero(Object o) {
114        if (priKeyFormat == null) {
115            throw new IllegalStateException
116                ("No primary key: " + o.getClass().getName());
117        }
118        return ((Enhanced) o).bdbIsPriKeyFieldNullOrZero();
119    }
120
121    public void writePriKeyField(Object o, EntityOutput output) {
122        if (priKeyFormat == null) {
123            throw new IllegalStateException
124                ("No primary key: " + o.getClass().getName());
125        }
126        ((Enhanced) o).bdbWritePriKeyField(output, priKeyFormat);
127    }
128
129    public void readPriKeyField(Object o, EntityInput input) {
130        if (priKeyFormat == null) {
131            throw new IllegalStateException
132                ("No primary key: " + o.getClass().getName());
133        }
134        ((Enhanced) o).bdbReadPriKeyField(input, priKeyFormat);
135    }
136
137    public void writeSecKeyFields(Object o, EntityOutput output) {
138        ((Enhanced) o).bdbWriteSecKeyFields(output);
139    }
140
141    public void readSecKeyFields(Object o,
142                                 EntityInput input,
143                                 int startField,
144                                 int endField,
145                                 int superLevel) {
146        ((Enhanced) o).bdbReadSecKeyFields
147            (input, startField, endField, superLevel);
148    }
149
150    public void writeNonKeyFields(Object o, EntityOutput output) {
151        ((Enhanced) o).bdbWriteNonKeyFields(output);
152    }
153
154    public void readNonKeyFields(Object o,
155                                 EntityInput input,
156                                 int startField,
157                                 int endField,
158                                 int superLevel) {
159        ((Enhanced) o).bdbReadNonKeyFields
160            (input, startField, endField, superLevel);
161    }
162
163    public Object getField(Object o,
164                           int field,
165                           int superLevel,
166                           boolean isSecField) {
167        return ((Enhanced) o).bdbGetField(o, field, superLevel, isSecField);
168    }
169
170    public void setField(Object o,
171                         int field,
172                         int superLevel,
173                         boolean isSecField,
174                         Object value) {
175        ((Enhanced) o).bdbSetField(o, field, superLevel, isSecField, value);
176    }
177}
178