001: /************************************************************************
002: Copyright 2004 Emil Kirschner
003:
004: Licensed under the Apache License, Version 2.0 (the "License");
005: you may not use this file except in compliance with the License.
006: You may obtain a copy of the License at
007:
008: http://www.apache.org/licenses/LICENSE-2.0
009:
010: Unless required by applicable law or agreed to in writing, software
011: distributed under the License is distributed on an "AS IS" BASIS,
012: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: See the License for the specific language governing permissions and
014: limitations under the License.
015: ************************************************************************/package com.thekirschners.uitoys.autoui;
016:
017: import com.thekirschners.uitoys.autoui.annotations.Editable;
018: import com.thekirschners.uitoys.autoui.annotations.EditorHints;
019: import com.thekirschners.uitoys.autoui.annotations.LabelHints;
020: import com.thekirschners.uitoys.autoui.annotations.SeparationHints;
021: import com.thekirschners.uitoys.autoui.editors.ValueEditor;
022: import com.thekirschners.uitoys.autoui.editors.ValueEditorsRegistry;
023: import com.thekirschners.uitoys.autoui.validators.DataValidationException;
024: import com.thekirschners.uitoys.autoui.i18n.LocalizerFactory;
025:
026: import javax.swing.*;
027: import java.awt.*;
028: import java.lang.annotation.Annotation;
029: import java.lang.reflect.Field;
030: import java.util.*;
031:
032: /**
033: * <p>automatically constructs an editor for an instance of a data object, provided that the specified class is
034: * decorated with proper annotations from the com.thekirschners.uitoys.uitools.autoui.annotations package
035: */
036: public class AutoPanel<DataType> extends JPanel {
037: /**
038: * <p>constructs an editor for the specified object. the objects must be annotated using annotations from the
039: * com.thekirschners.uitoys.uitools.autoui.annotations package
040: *
041: * @param editedObject the object for which the editor will be constructed
042: */
043: public AutoPanel(DataType editedObject) {
044: this .editedObject = editedObject;
045:
046: extractClassInformation(editedObject);
047: populateUI(editedObject);
048: }
049:
050: /**
051: * <p>replaces the previous object with the new one - the object must be of the same class;
052: *
053: * @param editedObject the new instance that must be edited
054: */
055: public void reset(DataType editedObject) {
056: assert this .editedObject.getClass().equals(
057: editedObject.getClass());
058:
059: this .editedObject = editedObject;
060:
061: Class cls = editedObject.getClass();
062:
063: Set<String> fieldSet = fieldEditorMapping.keySet();
064: for (String fieldName : fieldSet) {
065: Object newValue = null;
066: try {
067: Field fld = getClassField(cls, fieldName);
068: fld.setAccessible(true);
069: newValue = fld.get(editedObject);
070: } catch (IllegalAccessException e) {
071: e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
072: assert false;
073: }
074: initialValues.put(fieldName, newValue);
075: ValueEditor valueEditor = fieldEditorMapping.get(fieldName);
076: System.out.println("initialValue = " + newValue.toString());
077: valueEditor.setValue(newValue);
078: }
079: }
080:
081: /**
082: * <p>reverts editor content to initial values
083: */
084: public void reset() {
085: Set<String> fieldSet = fieldEditorMapping.keySet();
086: for (String fieldName : fieldSet) {
087: Object initialValue = initialValues.get(fieldName);
088: ValueEditor valueEditor = fieldEditorMapping.get(fieldName);
089: System.out.println("initialValue = "
090: + initialValue.toString());
091: valueEditor.setValue(initialValue);
092: }
093: }
094:
095: /**
096: * <p>applies editor values to the objects and returns it
097: */
098: public DataType getEditedObject() throws DataValidationException {
099: assert editedObject != null;
100:
101: Class editedObjectClass = editedObject.getClass();
102: Set<String> fieldSet = fieldEditorMapping.keySet();
103: for (String fieldName : fieldSet) {
104: Field field = null;
105: field = getClassField(editedObjectClass, fieldName);
106:
107: assert field != null;
108: ValueEditor valueEditor = fieldEditorMapping.get(fieldName);
109: Object value = valueEditor.getValue();
110: try {
111: field.setAccessible(true);
112: field.set(editedObject, value);
113: } catch (IllegalAccessException e) {
114: e.printStackTrace();
115: assert false;
116: }
117: }
118:
119: return editedObject;
120: }
121:
122: /**
123: * <p>returns the user interface name of the
124: *
125: * @return the user interface name as specified by annotations
126: */
127: public String getUiName() {
128: assert editedObject != null;
129: return uiName;
130: }
131:
132: private void populateUI(DataType editedObject) {
133: GridBagLayout gbl = new GridBagLayout();
134: setLayout(gbl);
135:
136: ArrayList<SortableUIElement> uiElements = buildUIElements(editedObject);
137: addUIElementsToPanel(uiElements, gbl);
138: revalidate();
139: }
140:
141: private ArrayList<SortableUIElement> buildUIElements(
142: DataType editedObject) {
143: ArrayList<SortableUIElement> uiElements = new ArrayList<SortableUIElement>();
144: if (fieldEditorMapping == null)
145: fieldEditorMapping = new HashMap<String, ValueEditor>();
146: else
147: fieldEditorMapping.clear();
148:
149: if (initialValues == null)
150: initialValues = new HashMap<String, Object>();
151: else
152: initialValues.clear();
153:
154: Class cls = editedObject.getClass();
155: Field[] fields = getClassFields(cls);
156: System.out.println("fields.lenght = " + fields.length);
157: for (Field field : fields) {
158: Annotation annotation = field
159: .getAnnotation(EditorHints.class);
160: if (annotation != null) {
161: EditorHints editorHints = (EditorHints) annotation;
162: int order = editorHints.order();
163:
164: Class declaringClass = field.getDeclaringClass();
165: Annotation declaringClassAnnotation = declaringClass
166: .getAnnotation(Editable.class);
167: assert declaringClassAnnotation != null;
168: Editable editableClass = (Editable) declaringClassAnnotation;
169: int orderBase = editableClass.orderBase();
170:
171: ValueEditor editor = ValueEditorsRegistry.getInstance()
172: .getValueEditor(field.getType());
173: assert editor != null : "value editors factory returned null editor for type: "
174: + field.getType().getName();
175: JComponent comp = editor.getEditorComponent();
176: assert comp != null : "value editor ["
177: + editor.getClass().getName()
178: + "] returned null JComponent";
179:
180: fieldEditorMapping.put(field.getName(), editor);
181: storeFieldValue(field, editedObject);
182:
183: GridBagConstraints gbc = gridBagConstraintsFromAnnotation(editorHints);
184:
185: SortableUIElement sortable = new SortableUIElement(
186: order, orderBase, comp, gbc);
187:
188: applyLabelHints(field, sortable);
189: applySeparationHints(field, sortable);
190:
191: uiElements.add(sortable);
192: }
193: }
194:
195: Collections.sort((java.util.List) uiElements);
196: return uiElements;
197: }
198:
199: private void storeFieldValue(Field field, DataType editedObject) {
200: Object fieldValue = null;
201: try {
202: field.setAccessible(true);
203: fieldValue = field.get(editedObject);
204: } catch (IllegalAccessException e) {
205: assert false : "make sure setAccessible is set to true";
206: }
207: initialValues.put(field.getName(), fieldValue);
208: }
209:
210: private void applyLabelHints(Field field, SortableUIElement sortable) {
211: JLabel labelBefore = null, labelAfter = null;
212: GridBagConstraints beforeConstraints = null, afterConstraints = null;
213:
214: Annotation labelAnnotation = field
215: .getAnnotation(LabelHints.class);
216: if (labelAnnotation != null) {
217: LabelHints lbl = (LabelHints) labelAnnotation;
218: String localizedUILabel = LocalizerFactory.getInstance()
219: .getLocalizer().getLocalizedString(lbl.uiLabel());
220: if (lbl.placement() == LabelHints.Placement.BEFORE) {
221: labelBefore = new JLabel(localizedUILabel);
222: beforeConstraints = gridBagConstraintsFromLabelAnnotation(lbl);
223: sortable.setBeforeLabel(labelBefore, beforeConstraints);
224: } else {
225: labelAfter = new JLabel(localizedUILabel);
226: afterConstraints = gridBagConstraintsFromLabelAnnotation(lbl);
227: sortable.setAfterLabel(labelAfter, afterConstraints);
228: }
229: }
230: }
231:
232: private void applySeparationHints(Field field,
233: SortableUIElement sortable) {
234: Annotation separatorAnnotation = field
235: .getAnnotation(SeparationHints.class);
236: if (separatorAnnotation != null) {
237: SeparationHints separationHints = (SeparationHints) separatorAnnotation;
238: if (separationHints.placement() == SeparationHints.Placement.AFTER)
239: sortable.separateAfter();
240: else
241: sortable.separateBefore();
242: }
243: }
244:
245: private GridBagConstraints gridBagConstraintsFromAnnotation(
246: EditorHints editorHints) {
247: GridBagConstraints gbc = new GridBagConstraints();
248:
249: if (editorHints.anchor() != Integer.MIN_VALUE)
250: gbc.anchor = editorHints.anchor();
251:
252: gbc.fill = editorHints.fill();
253:
254: if (editorHints.gridy() != Integer.MIN_VALUE)
255: gbc.gridx = editorHints.gridx();
256: if (editorHints.gridy() != Integer.MIN_VALUE)
257: gbc.gridy = editorHints.gridy();
258:
259: gbc.gridheight = editorHints.gridheight();
260: gbc.gridwidth = editorHints.gridwidth();
261: gbc.weightx = editorHints.weightx();
262: gbc.weighty = editorHints.weighty();
263: gbc.insets.set(editorHints.insets()[0],
264: editorHints.insets()[1], editorHints.insets()[2],
265: editorHints.insets()[3]);
266: return gbc;
267: }
268:
269: private GridBagConstraints gridBagConstraintsFromLabelAnnotation(
270: LabelHints labelDescriptorHints) {
271: GridBagConstraints gbc = new GridBagConstraints();
272:
273: if (labelDescriptorHints.anchor() != Integer.MIN_VALUE)
274: gbc.anchor = labelDescriptorHints.anchor();
275:
276: gbc.fill = labelDescriptorHints.fill();
277:
278: if (labelDescriptorHints.gridy() != Integer.MIN_VALUE)
279: gbc.gridx = labelDescriptorHints.gridx();
280: if (labelDescriptorHints.gridy() != Integer.MIN_VALUE)
281: gbc.gridy = labelDescriptorHints.gridy();
282:
283: gbc.gridheight = labelDescriptorHints.gridheight();
284: gbc.gridwidth = labelDescriptorHints.gridwidth();
285: gbc.weightx = labelDescriptorHints.weightx();
286: gbc.weighty = labelDescriptorHints.weighty();
287: gbc.insets.set(labelDescriptorHints.insets()[0],
288: labelDescriptorHints.insets()[1], labelDescriptorHints
289: .insets()[2], labelDescriptorHints.insets()[3]);
290: return gbc;
291: }
292:
293: private void addUIElementsToPanel(
294: ArrayList<SortableUIElement> uiElements, GridBagLayout gbl) {
295: for (SortableUIElement element : uiElements)
296: element.applyUIElementToContainer(this , gbl);
297: }
298:
299: private void extractClassInformation(DataType editedObject) {
300: if (uiName != null)
301: return;
302:
303: Annotation markerAnnotation = editedObject.getClass()
304: .getAnnotation(Editable.class);
305: if (markerAnnotation == null)
306: throw new IncompatibleDataTypeException();
307: else {
308: Editable editable = (Editable) markerAnnotation;
309: uiName = editable.uiName();
310: }
311: }
312:
313: private Field getClassField(Class cls, String fieldName) {
314: Field fld = null;
315: try {
316: fld = cls.getDeclaredField(fieldName);
317: return fld;
318: } catch (NoSuchFieldException e) {
319: Class super Class = cls.getSuperclass();
320: if (super Class.equals(Object.class))
321: return null;
322: return getClassField(super Class, fieldName);
323: }
324: }
325:
326: private Field[] getClassFields(Class cls) {
327: ArrayList<Field> bag = new ArrayList<Field>();
328: getClassFields(cls, bag);
329:
330: int size = bag.size();
331: Field[] ret = new Field[size];
332: for (int i = 0; i < size; i++) {
333: ret[i] = bag.get(i);
334: }
335:
336: return ret;
337: }
338:
339: private void getClassFields(Class cls, ArrayList<Field> fieldSet) {
340: Field[] fields = cls.getDeclaredFields();
341: for (Field field : fields)
342: fieldSet.add(field);
343:
344: Class super Class = cls.getSuperclass();
345: if (!super Class.equals(Object.class))
346: getClassFields(super Class, fieldSet);
347: }
348:
349: private String uiName = null;
350: private DataType editedObject = null;
351: private Map<String, ValueEditor> fieldEditorMapping = null;
352: private Map<String, Object> initialValues = null;
353:
354: }
|