001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.form;
043:
044: import java.beans.*;
045: import java.lang.reflect.Constructor;
046: import java.lang.reflect.Modifier;
047: import java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.HashMap;
050: import java.util.LinkedList;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.WeakHashMap;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056:
057: /**
058: * Takes care of registration and finding property editors used by the Form Editor.
059: *
060: * @author Ian Formanek, Tomas Pavek
061: */
062: final public class FormPropertyEditorManager {
063:
064: /**
065: * Maps property type to property editor class. There are 2 maps - one for
066: * findings of the standard PropertyEditorManager, one for our specific
067: * search path (org.netbeans.modules.form.editors2 etc).
068: */
069: private static Map<Class, Class>[] editorClassCache;
070:
071: /**
072: * Maps property type to list of property editor classes. For externally
073: * registered editors (like i18n).
074: */
075: private static Map<Class, List<Class>> expliciteEditors;
076:
077: // -------
078:
079: public static synchronized PropertyEditor findEditor(
080: FormProperty property) {
081: Class type = property.getValueType();
082: FormModel form = property.getPropertyContext().getFormModel();
083: List<PropertyEditor> list = findEditors(type, form, false);
084: return list.isEmpty() ? null : list.get(0);
085: }
086:
087: public static synchronized PropertyEditor[] getAllEditors(
088: FormProperty property) {
089: Class type = property.getValueType();
090: FormModel form = property.getPropertyContext().getFormModel();
091: List<PropertyEditor> list = findEditors(type, form, true);
092: return list.toArray(new PropertyEditor[list.size()]);
093: }
094:
095: public static synchronized void registerEditor(Class propertyType,
096: Class editorClass) {
097: List<Class> classList;
098: if (expliciteEditors != null) {
099: classList = expliciteEditors.get(propertyType);
100: } else {
101: classList = null;
102: expliciteEditors = new HashMap<Class, List<Class>>();
103: }
104: if (classList == null) {
105: classList = new LinkedList<Class>();
106: classList.add(editorClass);
107: expliciteEditors.put(propertyType, classList);
108: } else if (!classList.contains(editorClass)) {
109: classList.add(editorClass);
110: }
111: }
112:
113: private static List<Class> getRegisteredEditorClasses(
114: Class propertyType) {
115: List<Class> classList = expliciteEditors != null ? expliciteEditors
116: .get(propertyType)
117: : null;
118: return classList != null ? classList : Collections.EMPTY_LIST;
119: }
120:
121: // -------
122:
123: private static List<PropertyEditor> findEditors(Class type,
124: FormModel targetForm, boolean all) {
125: List<PropertyEditor> editorList = new ArrayList<PropertyEditor>(
126: 5);
127:
128: // 1st - try standard way through PropertyEditorManager
129: if (isEditorInCache(type, 0)) {
130: createEditorFromCache(type, 0, editorList);
131: } else {
132: PropertyEditor editor = (type != Object.class && !hasWrappingEditor(type)) ? PropertyEditorManager
133: .findEditor(type)
134: : null;
135: if (editor != null) {
136: editorList.add(editor);
137: }
138: addEditorToCache(type, editor, 0); // also cache nonexistence
139: }
140: if (!all && !editorList.isEmpty()) {
141: return editorList;
142: }
143:
144: // 2nd - search the form editor specific search path
145: if (isEditorInCache(type, 1)) {
146: createEditorFromCache(type, 1, editorList);
147: } else {
148: String typeName = type.getSimpleName();
149: if (!typeName.contains("[")) { // not an array type // NOI18N
150: Class editorClass = null;
151: for (String path : FormLoaderSettings.getInstance()
152: .getEditorSearchPath()) {
153: String name = path + "." + typeName + "Editor"; // NOI18N
154: try {
155: editorClass = FormUtils.loadClass(name,
156: targetForm);
157: if (createEditorInstance(editorClass,
158: editorList)) {
159: break;
160: } else { // failed instantiating
161: editorClass = null;
162: }
163: } catch (Exception e) {
164: } // silently ignore
165: catch (LinkageError e) {
166: } // silently ignore
167: }
168: addEditorClassToCache(type, editorClass, 1); // also cache nonexistence
169: } // ignore array types
170: }
171: if (!all && !editorList.isEmpty()) {
172: return editorList;
173: }
174:
175: // 3rd - add editors registered using registerEditor(...)
176: for (Class cls : getRegisteredEditorClasses(type)) {
177: createEditorInstance(cls, editorList);
178: if (!all) {
179: return editorList;
180: }
181: }
182:
183: // 4th - add ComponentChooserEditor
184: if (editorList.isEmpty() && isComponentType(type)) {
185: editorList.add(new ComponentChooserEditor(
186: new Class[] { type }));
187: if (!all) {
188: return editorList;
189: }
190: }
191:
192: // 5th - add the RADConnectionPropertyEditor - twice
193: editorList.add(new RADConnectionPropertyEditor(type,
194: RADConnectionPropertyEditor.Type.FormConnection));
195: editorList.add(new RADConnectionPropertyEditor(type,
196: RADConnectionPropertyEditor.Type.CustomCode));
197:
198: return editorList;
199: }
200:
201: private static boolean isEditorInCache(Class propertyType, int level) {
202: return getEditorClassCache(level).containsKey(propertyType);
203: // the cache may also hold the information that there is no property editor for given type
204: }
205:
206: private static void createEditorFromCache(Class propertyType,
207: int level, List<PropertyEditor> list) {
208: Class editorClass = getEditorClassCache(level)
209: .get(propertyType);
210: if (editorClass != null) {
211: createEditorInstance(editorClass, list);
212: }
213: }
214:
215: private static void addEditorToCache(Class propertyType,
216: PropertyEditor editor, int level) {
217: if (editor == null) {
218: addEditorClassToCache(propertyType, null, level);
219: } else {
220: // Caching the class for editor instance is a bit tricky - the instance
221: // is created by PropertyEditorManager, but we may not be able to re-create
222: // it just from a class. We assume it is possible if the class has a no-arg
223: // public constructor. Otherwise we don't cache the property editor class.
224: Class editorClass = editor.getClass();
225: try {
226: Constructor ctor = editorClass.getConstructor();
227: if (ctor != null
228: && (ctor.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC) {
229: addEditorClassToCache(propertyType, editorClass,
230: level);
231: }
232: } catch (NoSuchMethodException ex) {
233: } // ignore
234: }
235: }
236:
237: private static void addEditorClassToCache(Class propertyType,
238: Class editorClass, int level) {
239: getEditorClassCache(level).put(propertyType, editorClass);
240: }
241:
242: private static Map<Class, Class> getEditorClassCache(int level) {
243: if (editorClassCache == null) {
244: editorClassCache = new Map[] {
245: new WeakHashMap<Class, Class>(),
246: new WeakHashMap<Class, Class>() };
247: // the weakness of the map is for classes from user projects
248: // (property types which we remember we have no property editor for)
249: }
250: return editorClassCache[level];
251: }
252:
253: /**
254: * Returns true if given type has a specific property editor wrapping the
255: * default one - so the default searching mechanism is not used to find
256: * such an editor. Used for subclasses of ResourceWrapperEditor.
257: */
258: private static boolean hasWrappingEditor(Class type) {
259: return type == String.class || type == java.awt.Font.class
260: || type == java.awt.Color.class
261: || type == javax.swing.Icon.class;
262: }
263:
264: /**
265: * Returns true if given type can be considered as "component type" - i.e.
266: * can expect components of this type in the form, so it makes sense to
267: * have ComponentChooserEditor as one of the property editors for this type.
268: */
269: private static boolean isComponentType(Class type) {
270: return !type.equals(Object.class) && !type.equals(String.class)
271: && !type.isEnum() && !type.isPrimitive()
272: && !Number.class.isAssignableFrom(type);
273: }
274:
275: private static boolean createEditorInstance(Class cls,
276: List<PropertyEditor> list) {
277: try {
278: list.add((PropertyEditor) cls.newInstance());
279: return true;
280: } catch (Exception ex) {
281: log(ex, "Error instantiating property editor: "
282: + cls.getName()); // NOI18N
283: } catch (LinkageError ex) {
284: log(ex, "Error instantiating property editor: "
285: + cls.getName()); // NOI18N
286: }
287: return false;
288: }
289:
290: private static Logger logger;
291:
292: private static void log(Throwable ex, String msg) {
293: if (logger == null) {
294: logger = Logger.getLogger(FormPropertyEditorManager.class
295: .getName());
296: }
297: logger.log(Level.INFO, msg, ex);
298: }
299: }
|