001: // $Id$
002:
003: package net.sf.persist;
004:
005: import java.lang.reflect.Method;
006: import java.util.HashMap;
007: import java.util.Map;
008: import java.util.Set;
009:
010: /**
011: * Represents the mapping of columns to getters and setters of a POJO.
012: * <p>
013: * It is used when a class specifies a
014: * {@link net.sf.persist.annotations.NoTable NoTable} annotation, which means
015: * the class is not mapped to a table in the database, and will be only used to
016: * store data from queries.
017: */
018: public class NoTableMapping extends Mapping {
019:
020: // POJO class
021: private final Class objectClass;
022:
023: // map field names to setters
024: private final Map<String, Method> settersMap;
025:
026: // map field names to getters
027: private final Map<String, Method> gettersMap;
028:
029: // map possible column names to field names
030: private final Map<String, String> columnsMap;
031:
032: public NoTableMapping(Class objectClass, NameGuesser nameGuesser) {
033:
034: checkAnnotation(objectClass);
035:
036: this .objectClass = objectClass;
037:
038: // get the list of annotations, getters and setters
039: Map[] fieldsMaps = Mapping.getFieldsMaps(objectClass);
040: final Map<String, net.sf.persist.annotations.Column> annotationsMap = fieldsMaps[0];
041: gettersMap = fieldsMaps[1];
042: settersMap = fieldsMaps[2];
043:
044: // create columns map by iterating through all fields in the object
045: // class
046: // if a field has a @Column annotation, use it, otherwise add all
047: // guessed names for the field in the map
048: columnsMap = new HashMap();
049: for (String fieldName : gettersMap.keySet()) {
050:
051: net.sf.persist.annotations.Column annotation = annotationsMap
052: .get(fieldName);
053:
054: // autoGenerated is not supported on @NoTable mappings
055: if (annotation != null) {
056: if (annotation.autoGenerated() == true) {
057: throw new PersistException(
058: "@Column(autoGenerated=true) is set for field ["
059: + fieldName
060: + "] of class ["
061: + objectClass.getCanonicalName()
062: + " which has been declared with @NoTable");
063: }
064: }
065:
066: // if there's a column name specified in the annotation, use it
067: if (annotation != null && annotation.name() != null) {
068:
069: // check if the column name is blank
070: if (annotation.name().trim().equals("")) {
071: throw new PersistException(
072: "@Column annotation for field ["
073: + fieldName + "] of class ["
074: + objectClass.getCanonicalName()
075: + "] defines a blank column name");
076: }
077:
078: // check for name conflicts
079: checkNameConflicts(fieldName, annotation.name());
080:
081: // add to map
082: columnsMap.put(annotation.name(), fieldName);
083: }
084:
085: else { // no annotation, add all guessed column names for the field
086:
087: Set<String> guessedColumns = nameGuesser
088: .guessColumn(fieldName);
089: for (String guessedColumn : guessedColumns) {
090:
091: // check for name conflicts
092: checkNameConflicts(fieldName, guessedColumn);
093:
094: // add to map
095: columnsMap.put(guessedColumn, fieldName);
096: }
097: }
098: }
099:
100: }
101:
102: /**
103: * Returns the field name associated with a given column. If a mapping can't
104: * be found, will throw a PersistException.
105: */
106: public String getFieldNameForColumn(String columnName) {
107: String fieldName = columnsMap.get(columnName);
108: if (fieldName == null) {
109: throw new PersistException(
110: "Could map field for column ["
111: + columnName
112: + "] on class ["
113: + objectClass.getCanonicalName()
114: + "]. Please specify an explict @Column annotation for that column.");
115: }
116: return fieldName;
117: }
118:
119: /**
120: * Returns the setter method associated with a given column. If a mapping
121: * can't be found, will throw a PersistException.
122: *
123: * @see Mapping
124: */
125: public Method getSetterForColumn(String columnName) {
126: String fieldName = getFieldNameForColumn(columnName);
127: return settersMap.get(fieldName);
128: }
129:
130: /**
131: * Returns the getter method associated with a given column. If a mapping
132: * can't be found, will throw a PersistException.
133: *
134: * @see Mapping
135: */
136: public Method getGetterForColumn(String columnName) {
137: String fieldName = getFieldNameForColumn(columnName);
138: return gettersMap.get(fieldName);
139: }
140:
141: /**
142: * Checks if a given column name conflicts with an existing name in the
143: * columns map.
144: */
145: private void checkNameConflicts(String fieldName, String column) {
146: String existingFieldName = columnsMap.get(column);
147: if (existingFieldName != null) {
148: throw new PersistException(
149: "Fields ["
150: + fieldName
151: + "] and ["
152: + existingFieldName
153: + "] have conflicting column name ["
154: + column
155: + "] either from guessed names or anotations. "
156: + "Please specify @Column mappings for at least one of those fields");
157: }
158: }
159:
160: /**
161: * Checks if {@link net.sf.persist.annotations.NoTable NoTable} is present
162: * and if a conflicting {@link net.sf.persist.annotations.Table Table} is
163: * not present.
164: */
165: private void checkAnnotation(Class objectClass) {
166: // get @NoTable annotation
167: final net.sf.persist.annotations.NoTable noTableAnnotation = (net.sf.persist.annotations.NoTable) objectClass
168: .getAnnotation(net.sf.persist.annotations.NoTable.class);
169:
170: // check if annotation is set
171: if (noTableAnnotation == null) {
172: throw new PersistException(
173: "Class ["
174: + objectClass.getCanonicalName()
175: + "] does not specify a @NoTable annotation therefore it can't be mapped through NoTableMapping");
176: }
177:
178: // check for conflicting @Table annotation
179: final net.sf.persist.annotations.Table tableAnnotation = (net.sf.persist.annotations.Table) objectClass
180: .getAnnotation(net.sf.persist.annotations.Table.class);
181:
182: if (tableAnnotation != null) {
183: throw new PersistException(
184: "Class ["
185: + objectClass.getCanonicalName()
186: + "] specifies conflicting @Table and @NoTable annotations");
187: }
188: }
189:
190: }
|