001: package org.conform.wings;
002:
003: import java.util.*;
004: import java.text.MessageFormat;
005:
006: import org.conform.*;
007: import org.conform.format.ResourceProvider;
008: import org.conform.wings.editor.DefaultEditorStyles;
009: import org.wings.*;
010: import org.wings.util.SessionLocal;
011: import org.wings.event.SParentFrameListener;
012: import org.wings.event.SParentFrameEvent;
013: import org.wings.table.*;
014: import org.wingx.table.XTableColumn;
015: import org.apache.commons.logging.LogFactory;
016:
017: /**
018: * // TODO: make this a DefaultComponentFactory implementing ComponentFactory
019: * @author gabriel pantazi
020: */
021: public class ComponentFactory {
022: private static org.apache.commons.logging.Log LOG = LogFactory
023: .getLog(ComponentFactory.class);
024:
025: public static final ComponentFactory INSTANCE = new ComponentFactory();
026:
027: protected EditorFactory editorFactory;
028: private ResourceProvider resourceProvider;
029:
030: public void setEditorFactory(EditorFactory editorFactory) {
031: this .editorFactory = editorFactory;
032: }
033:
034: /**
035: * Creates a graphical component based on the meta information and on data information for
036: * the specified property name.
037: *
038: * @param beanMeta the whole meta information for a certain class used to initialize the meta
039: * @param beanData the whole data information
040: * @param propertyName property name used to generate the component for
041: * @return the SComponent generated for the specified property.
042: */
043: public SComponent createComponent(BeanMeta beanMeta,
044: BeanData beanData, String propertyName) {
045: try {
046: PropertyMeta propertyMeta = beanMeta
047: .getProperty(propertyName);
048: if (propertyMeta == null)
049: throw new IllegalArgumentException(beanMeta.getName()
050: + " has no propertyMeta named " + propertyName);
051:
052: Editor editor = getEditor(propertyMeta);
053: SComponent component = editor.createComponent(propertyMeta);
054: component.setVerticalAlignment(SConstants.TOP_ALIGN);
055: component.setHorizontalAlignment(SConstants.LEFT_ALIGN);
056:
057: PropertyData propertyData = beanData
058: .getPropertyData(propertyMeta);
059: if (propertyData != null)
060: editor.setPropertyData(component, propertyData);
061:
062: beanData
063: .addValidationListener(new InplaceValidationListener(
064: editor, component, propertyMeta));
065:
066: component.addParentFrameListener(new StableNaming(
067: component, beanMeta, propertyName));
068: return component;
069: } catch (RuntimeException e) {
070: LOG.error("Exception creating Component for "
071: + beanMeta.getName() + "." + propertyName);
072: throw e;
073: }
074: }
075:
076: public SComponent createReadOnlyComponent(BeanMeta beanMeta,
077: BeanData beanData, String name) {
078: try {
079: PropertyMeta propertyMeta = beanMeta.getProperty(name);
080: if (propertyMeta == null)
081: throw new IllegalArgumentException(beanMeta.getName()
082: + " has no propertyMeta named " + name);
083:
084: Editor editor = getReadOnlyEditor(propertyMeta);
085: SComponent component = editor.createComponent(propertyMeta);
086:
087: PropertyData propertyData = beanData
088: .getPropertyData(propertyMeta);
089: if (propertyData != null)
090: editor.setPropertyData(component, propertyData);
091:
092: return component;
093: } catch (RuntimeException e) {
094: LOG.error("Exception creating readonly Component for "
095: + beanMeta.getName() + "." + name);
096: throw e;
097: }
098: }
099:
100: public void configureComponent(PropertyMeta propertyMeta,
101: SComponent component, boolean errorneus) {
102: Editor editor = getEditor(propertyMeta);
103: editor.configureComponent(propertyMeta, component, false);
104: }
105:
106: /**
107: * Creates the apropriate label for the specified property name. If the label is not set (null)
108: * a label with the property name will be returned.
109: *
110: * @param beanMeta the meta information with localized label and micro help
111: * @param name property name used to generate the label for.
112: * @return the SLabel generated for the specified property.
113: */
114: public SLabel createLabel(BeanMeta beanMeta, String name) {
115: PropertyMeta propertyMeta = beanMeta.getProperty(name);
116: if (propertyMeta == null)
117: throw new IllegalArgumentException(beanMeta.getName()
118: + " has no propertyMeta named " + name);
119:
120: SLabel label = new SLabel();
121: label.setVerticalAlignment(SConstants.TOP_ALIGN);
122: DefaultEditorStyles.applyLabelState(propertyMeta, label);
123:
124: String text = propertyMeta.getLabel();
125: label.setText(text != null ? text : propertyMeta.getName());
126: label.setToolTipText(propertyMeta.getMicroHelp());
127: return label;
128: }
129:
130: public void configureLabel(PropertyMeta propertyMeta, SLabel label,
131: boolean errorneus) {
132: DefaultEditorStyles.applyLabelState(propertyMeta, label);
133: }
134:
135: /**
136: * Creates a STableColumn for the specified property name.
137: *
138: * @param beanMeta the whole meta information
139: * @param name the property name for which the STableColumn is generated.
140: * @param modelIndex Sets the cmp2 index for this column. The cmp2 index is the
141: * index of the column in the cmp2 that will be displayed by this
142: * <code>STableColumn</code>. As the <code>STableColumn</code>
143: * is moved around in the view the cmp2 index remains constant.
144: * @return the STableColumn properly initialized based on the given property name and meta information.
145: */
146: public XTableColumn createTableColumn(BeanMeta beanMeta,
147: String name, int modelIndex) {
148: try {
149: PropertyMeta property = beanMeta.getProperty(name);
150: if (property == null)
151: throw new IllegalArgumentException(beanMeta.getName()
152: + " has no property named " + name);
153:
154: Editor editor = getEditor(property);
155: Editor readOnlyEditor = getReadOnlyEditor(property);
156:
157: STableCellEditor cellEditor = null;
158: if (property.isWritable())
159: cellEditor = new PropertyCellEditor(editor, property);
160:
161: STableCellRenderer cellRenderer = new PropertyCellRenderer(
162: readOnlyEditor, property);
163: XTableColumn column = new XTableColumn(modelIndex, null,
164: cellRenderer, cellEditor);
165: column.setHidden(!property.isReadable());
166: column.setIdentifier(property.getName());
167: String label = property.getLabel();
168: column.setHeaderValue(label != null ? label : property
169: .getName());
170: column.setWidth(getWidth(property));
171: return column;
172: } catch (RuntimeException e) {
173: LOG.error("Exception creating Column for "
174: + beanMeta.getName() + "." + name);
175: throw e;
176: }
177: }
178:
179: /**
180: * Creates a List of STableColum(s) based on the provided BeanMeta. For each property meta from bean meta
181: * a new STableColumn will be added to the list.
182: *
183: * @param beanMeta the whole meta information
184: * @return a List of STableColumn(s)
185: */
186: public List createTableColumns(BeanMeta beanMeta) {
187: List columns = new ArrayList();
188: for (int i = 0; i < beanMeta.getProperties().length; i++) {
189: PropertyMeta propertyMeta = beanMeta.getProperties()[i];
190: XTableColumn tableColumn = createTableColumn(beanMeta,
191: propertyMeta.getName(), columns.size());
192: columns.add(tableColumn);
193: }
194: return columns;
195: }
196:
197: public Editor getEditor(PropertyMeta property) {
198: if (editorFactory == null)
199: editorFactory = new DefaultEditorFactory();
200: return editorFactory.getEditor(property);
201: }
202:
203: public Editor getReadOnlyEditor(PropertyMeta property) {
204: if (editorFactory == null)
205: editorFactory = new DefaultEditorFactory();
206: return editorFactory.getReadOnlyEditor(property);
207: }
208:
209: protected String getWidth(PropertyMeta propertyMeta) {
210: if (Date.class.isAssignableFrom(propertyMeta.getType()))
211: return "80";
212: else if (boolean.class.equals(propertyMeta.getType())
213: || Boolean.class.equals(propertyMeta.getType()))
214: return "60";
215: else if (propertyMeta
216: .getAttribute(PropertyMeta.ATTRIBUTE_PRECISION) != null) {
217: int cols = 1 + (Integer) propertyMeta
218: .getAttribute(PropertyMeta.ATTRIBUTE_PRECISION);
219: if (propertyMeta.getAttribute(PropertyMeta.ATTRIBUTE_SCALE) != null)
220: cols += 1;
221: return cols * 10 + "";
222: } else if (propertyMeta
223: .getAttribute(PropertyMeta.ATTRIBUTE_LENGTH) != null) {
224: int cols = (Integer) propertyMeta
225: .getAttribute(PropertyMeta.ATTRIBUTE_LENGTH);
226: return cols > 32 ? "*" : cols * 10 + "";
227: } else
228: return null;
229: }
230:
231: public void setResourceProvider(ResourceProvider resourceProvider) {
232: this .resourceProvider = resourceProvider;
233: }
234:
235: private class InplaceValidationListener implements
236: ValidationListener {
237: Editor edior;
238: SComponent component;
239: PropertyMeta propertyMeta;
240:
241: Set<Issue> issues = new HashSet<Issue>();
242: StringBuilder detail = new StringBuilder();
243:
244: public InplaceValidationListener(Editor editor,
245: SComponent component, PropertyMeta propertyMeta) {
246: this .edior = editor;
247: this .component = component;
248: this .propertyMeta = propertyMeta;
249: }
250:
251: public void addIssues(ValidationEvent event) {
252: boolean change = false;
253: for (Issue issue : event.getIssues()) {
254: if (Arrays.asList(issue.getMetas()).contains(
255: propertyMeta)) {
256: issues.remove(issue);
257: issues.add(issue);
258: change = true;
259: }
260: }
261:
262: if (change)
263: update((Data) event.getSource());
264: }
265:
266: public void removeIssues(ValidationEvent event) {
267: boolean change = false;
268: for (Issue issue : event.getIssues()) {
269: if (Arrays.asList(issue.getMetas()).contains(
270: propertyMeta)) {
271: change = issues.remove(issue) || change;
272: }
273: }
274:
275: if (change)
276: update((Data) event.getSource());
277: }
278:
279: void update(Data data) {
280: if (data instanceof PropertyData)
281: data = ((PropertyData) data).getBeanData();
282:
283: update(data.isIssuePublishingActive());
284: }
285:
286: private void update(boolean issuePublishingActive) {
287: if (issuePublishingActive && issues.size() > 0) {
288: component.setToolTipText(composeToolTipText());
289: edior.configureComponent(propertyMeta, component, true);
290: } else {
291: component.setToolTipText(null);
292: edior
293: .configureComponent(propertyMeta, component,
294: false);
295: }
296: }
297:
298: public String composeToolTipText() {
299: detail.setLength(0);
300: for (Iterator<Issue> iterator = issues.iterator(); iterator
301: .hasNext();) {
302: Issue issue = iterator.next();
303: PropertyMeta[] metas = issue.getMetas();
304:
305: if (metas.length == 1) {
306: label(metas[0]);
307: detail.append(":\n");
308: } else {
309: for (int i = 0; i < metas.length; i++) {
310: if (i > 0)
311: detail.append(", ");
312: label(metas[i]);
313: }
314: detail.append(":\n");
315: }
316:
317: String code = issue.getCode();
318: Object[] arguments = issue.getArguments();
319:
320: if (code == null)
321: code = "validation.incorrect";
322:
323: code = resourceProvider.getMessage(component
324: .getSession().getLocale(), code);
325:
326: if (arguments == null)
327: detail.append(" - ").append(code);
328: else
329: detail.append(" - ").append(
330: MessageFormat.format(code, arguments));
331:
332: detail.append('\n');
333: }
334: return detail.toString();
335: }
336:
337: private void label(Meta meta) {
338: String label = meta.getLabel();
339: detail.append(label != null ? label : meta.getName());
340: }
341:
342: public void clearIssues(Meta meta) {
343: issues.clear();
344: update(false);
345: }
346: }
347:
348: /**
349: * a set of per session unique names
350: */
351: private static final SessionLocal usedComponentNames = new SessionLocal() {
352: @Override
353: protected Set<String> initialValue() {
354: return new HashSet<String>();
355: }
356: };
357:
358: private static class StableNaming implements SParentFrameListener {
359: private final SComponent component;
360: private final BeanMeta beanMeta;
361: private final String propertyName;
362:
363: public StableNaming(SComponent component, BeanMeta beanMeta,
364: String propertyName) {
365: this .component = component;
366: this .beanMeta = beanMeta;
367: this .propertyName = propertyName;
368: }
369:
370: public void parentFrameAdded(SParentFrameEvent e) {
371:
372: // unregister with the old name
373: if (component.getSession().getDispatcher() != null
374: && component instanceof LowLevelEventListener) {
375: component.getSession().getDispatcher().unregister(
376: (LowLevelEventListener) component);
377: }
378:
379: String name = createComponentName(beanMeta.getType()
380: .getSimpleName(), propertyName);
381: Set<String> existingNames = (Set<String>) usedComponentNames
382: .get();
383: if (!existingNames.add(name)) {
384: for (int i = 1;; i++) {
385: String tryName = name + NAME_SEPARATOR + i;
386: if (existingNames.add(tryName)) {
387: name = tryName;
388: break;
389: }
390: }
391: }
392:
393: // set the new name
394: component.setName(name);
395:
396: // unregister with the old name
397: if (component.getSession().getDispatcher() != null
398: && component instanceof LowLevelEventListener) {
399: component.getSession().getDispatcher().register(
400: (LowLevelEventListener) component);
401: }
402: }
403:
404: private final static String NAME_SEPARATOR = "";
405:
406: public void parentFrameRemoved(SParentFrameEvent e) {
407: ((Set) usedComponentNames.get())
408: .remove(component.getName());
409: }
410:
411: /**
412: * create a name from the class name and the property name
413: */
414: private String createComponentName(String className,
415: String propertyName) {
416: StringBuffer buffer = new StringBuffer(className);
417: buffer.append(NAME_SEPARATOR);
418: buffer.append(propertyName);
419: return buffer.toString();
420: }
421: }
422: }
|