0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.form;
0043:
0044: import java.awt.Component;
0045: import java.beans.PropertyEditor;
0046: import java.beans.PropertyEditorSupport;
0047: import java.io.IOException;
0048: import java.net.URL;
0049: import java.util.ArrayList;
0050: import java.util.Collection;
0051: import java.util.Collections;
0052: import java.util.HashMap;
0053: import java.util.Iterator;
0054: import java.util.LinkedList;
0055: import java.util.List;
0056: import java.util.ListIterator;
0057: import java.util.Map;
0058: import java.util.WeakHashMap;
0059: import org.netbeans.api.java.classpath.ClassPath;
0060: import org.netbeans.modules.form.editors2.BorderDesignSupport;
0061: import org.netbeans.modules.form.editors2.TableColumnModelEditor;
0062: import org.openide.ErrorManager;
0063: import org.openide.filesystems.FileObject;
0064: import org.openide.loaders.DataObject;
0065: import org.openide.nodes.Node;
0066: import org.openide.nodes.PropertySupport;
0067: import org.openide.util.Lookup;
0068:
0069: /**
0070: * This class manages resources of a form (i.e. property values stored
0071: * externally as resources). It communicates with the app framework support
0072: * via the ResourceService interface. It also takes care of the automatic
0073: * internationalization backed by the i18n/form module.
0074: *
0075: * @author Tomas Pavek
0076: */
0077: public class ResourceSupport {
0078:
0079: private FormModel formModel;
0080:
0081: private ResourceService resourceService;
0082: private I18nService i18nService;
0083:
0084: private String designLocale = ""; // locale suffix (including initial _)
0085: private static Map<DataObject, String> rememberedLocales = new WeakHashMap<DataObject, String>();
0086:
0087: private Map<String, Object> droppedValues;
0088:
0089: /** Marks property that should be excluded from resourcing (explicitly set
0090: * to plain value). */
0091: private static final String EXCLUDE_FROM_RESOURCING = "does not want to be a resource"; // NOI18N
0092: private static final String EXCLUSION_DETERMINED = "already consulted with exclusion filters"; // NOI18N
0093: private static final String[] PROPERTY_ATTRS = {
0094: EXCLUSION_DETERMINED, EXCLUDE_FROM_RESOURCING };
0095:
0096: static final String PROP_AUTO_SET_COMPONENT_NAME = "autoSetComponentName"; // NOI18N
0097:
0098: static final String PROP_AUTO_RESOURCING = "autoResourcing"; // NOI18N
0099: static final int AUTO_OFF = 0;
0100: static final int AUTO_I18N = 1;
0101: static final int AUTO_RESOURCING = 2;
0102: static final int AUTO_INJECTION = 3;
0103:
0104: static final String PROP_FORM_BUNDLE = "formBundle"; // NOI18N
0105: private static final String PROP_DESIGN_LOCALE = "designLocale"; // NOI18N
0106: private static final String DEFAULT_BUNDLE_NAME = "Bundle"; // NOI18N
0107: private String defaultI18nBundle;
0108:
0109: private static final int PLAIN_VALUE = 1;
0110: private static final int UNDEFINED_RESOURCE = 2; // key == COMPUTE_AUTO_KEY
0111: private static final int VALID_RESOURCE_VALUE = 4;
0112:
0113: static final FormProperty.Filter COPIED_PROPERTY_FILTER = new FormProperty.Filter() {
0114: public boolean accept(FormProperty property) {
0115: // don't copy name property
0116: return property.isChanged()
0117: && !isAutoNamedProperty(property);
0118: }
0119: };
0120:
0121: // -----
0122:
0123: ResourceSupport(FormModel formModel) {
0124: this .formModel = formModel;
0125: formModel.addFormModelListener(new ModelListener());
0126: }
0127:
0128: void init() {
0129: String locale = rememberedLocales.get(getSrcDataObject());
0130: if (locale != null) {
0131: designLocale = locale;
0132: if (!locale.equals("") && formModel.isFormLoaded()) // NOI18N
0133: updateDesignLocale();
0134: }
0135: }
0136:
0137: private ResourceService getResourceService() {
0138: if (resourceService == null) {
0139: resourceService = Lookup.getDefault().lookup(
0140: ResourceService.class);
0141: }
0142: return resourceService;
0143: }
0144:
0145: private I18nService getI18nService() {
0146: if (i18nService == null) {
0147: i18nService = Lookup.getDefault().lookup(I18nService.class);
0148: }
0149: return i18nService;
0150: }
0151:
0152: private static ResourceSupport getResourceSupport(
0153: FormProperty property) {
0154: return FormEditor.getResourceSupport(property
0155: .getPropertyContext().getFormModel());
0156: }
0157:
0158: private static ResourceSupport getResourceSupport(
0159: RADComponent metacomp) {
0160: return FormEditor.getResourceSupport(metacomp.getFormModel());
0161: }
0162:
0163: // -----
0164:
0165: /**
0166: * Prepares a newly created form.
0167: */
0168: void prepareNewForm() {
0169: if (isResourceAutoMode()) {
0170: resourceService.prepareNew(getSourceFile());
0171: } else if (!isI18nAutoMode()) {
0172: return;
0173: }
0174: switchFormToResources(); // templates don't contain internationalized texts or resources
0175: if (isAutoName()) {
0176: setupNameProperty(true);
0177: }
0178: }
0179:
0180: /**
0181: * Converts given value to a resource. Called always when a component
0182: * property is being set.
0183: * This method is not called during undo/redo.
0184: * @param value new value being set to the property (can be FormProperty.ValueWithEditor)
0185: * @param property the property to which the value is going to be set (still
0186: * contains the previous value)
0187: */
0188: public static Object makeResource(Object value,
0189: FormProperty property) {
0190: if (!isResourceableProperty(property))
0191: return value;
0192:
0193: ResourceSupport support = getResourceSupport(property);
0194: if (isResourceType(property.getValueType())) {
0195: value = support.makeResource0(value, property);
0196: } else { // check nested values (borders are the only meaningful example)
0197: // the same value is returned, but might have been changed inside
0198: for (FormProperty prop : support
0199: .getNestedResourceProperties(FormProperty
0200: .getEnclosedValue(value), property,
0201: PLAIN_VALUE)) {
0202: boolean fire = prop.isChangeFiring();
0203: prop.setChangeFiring(false);
0204: try {
0205: Object val = prop.getValue();
0206: Object resValue = support.makeResource0(val, prop);
0207: if (resValue != val) {
0208: prop.setValue(resValue);
0209:
0210: if (resValue instanceof ResourceValue) {
0211: // raise form version here - FormProperty doesn't do
0212: // that because firing is off
0213: support.formModel.raiseVersionLevel(
0214: FormModel.FormVersion.NB60,
0215: FormModel.FormVersion.NB60);
0216: }
0217: }
0218: } catch (Exception ex) {
0219: ErrorManager.getDefault().notify(
0220: ErrorManager.INFORMATIONAL, ex);
0221: }
0222: prop.setChangeFiring(fire);
0223: }
0224: }
0225: return value;
0226: // the value is set to the property which leads to call of updateStoredValue
0227: }
0228:
0229: private Object makeResource0(Object newValue, FormProperty property) {
0230: Object value = FormProperty.getEnclosedValue(newValue);
0231:
0232: if (value instanceof ExternalValue) {
0233: // when a copy of resource or i18n value is created, it may have the
0234: // key set to COMPUTE_AUTO_KEY asking form editor to provide it
0235: ExternalValue eValue = (ExternalValue) value;
0236: if (eValue.getKey() == ExternalValue.COMPUTE_AUTO_KEY) {
0237: if (value instanceof I18nValue
0238: && getI18nService() != null) {
0239: String key = getDefaultKey0(property, AUTO_I18N);
0240: eValue = i18nService.changeKey((I18nValue) value,
0241: key);
0242: } else if (value instanceof ResourceValue
0243: && getResourceService() != null) {
0244: String key = getDefaultKey0(property,
0245: AUTO_RESOURCING);
0246: eValue = resourceService.changeKey(
0247: (ResourceValue) value, key);
0248: }
0249: return eValue;
0250: } else {
0251: return newValue;
0252: }
0253: }
0254:
0255: if (value == null || !isConvertibleToResource(value)
0256: || isExcludedProperty0(property))
0257: return newValue;
0258:
0259: Object prevValue;
0260: try {
0261: prevValue = property.getValue();
0262: } catch (Exception ex) { // should not happen
0263: ErrorManager.getDefault().notify(
0264: ErrorManager.INFORMATIONAL, ex);
0265: return newValue;
0266: }
0267:
0268: if (prevValue instanceof I18nValue) {
0269: if (getI18nService() != null) {
0270: newValue = i18nService.changeValue(
0271: (I18nValue) prevValue, value.toString());
0272: }
0273: } else if (prevValue instanceof ResourceValue) {
0274: if (getResourceService() != null) {
0275: newValue = resourceService.changeValue(
0276: (ResourceValue) prevValue, value,
0277: getStringValue(property, value));
0278: }
0279: } else if (isI18nAutoMode()) {
0280: if (value instanceof String) {
0281: I18nValue i18nValue = searchDroppedI18nValue(property,
0282: value.toString());
0283: if (i18nValue == null) {
0284: i18nValue = i18nService.create(getDefaultKey0(
0285: property, AUTO_I18N), value.toString(),
0286: getSrcDataObject());
0287: }
0288: // also need to switch to the i18n property editor
0289: newValue = new FormProperty.ValueWithEditor(i18nValue,
0290: i18nService.getPropertyEditor(property
0291: .getValueType(), property
0292: .getCurrentEditor()));
0293: }
0294: } else if (isResourceAutoMode()) {
0295: // resource value does not have a particular property editor - respect
0296: // the one comming from outside
0297: PropertyEditor newPrEd = newValue instanceof FormProperty.ValueWithEditor ? ((FormProperty.ValueWithEditor) newValue)
0298: .getPropertyEditor()
0299: : null;
0300:
0301: ResourceValue resValue = searchDroppedResourceValue(
0302: property, value);
0303: if (resValue == null) {
0304: resValue = resourceService.create(getDefaultKey0(
0305: property, AUTO_RESOURCING), property
0306: .getValueType(), value, getStringValue(
0307: property, value), getSourceFile());
0308: }
0309: newValue = newPrEd != null ? new FormProperty.ValueWithEditor(
0310: resValue, newPrEd)
0311: : resValue;
0312: }
0313:
0314: return newValue;
0315: }
0316:
0317: private I18nValue searchDroppedI18nValue(FormProperty property,
0318: String expectedValue) {
0319: if (droppedValues != null) {
0320: String dropKey = getPropertyPath(property, null);
0321: Object value = droppedValues.get(dropKey);
0322: if (value instanceof I18nValue) {
0323: I18nValue i18nValue = (I18nValue) value;
0324: if (i18nValue.getValue().equals(expectedValue))
0325: return i18nValue;
0326: }
0327: }
0328: return null;
0329: }
0330:
0331: private ResourceValue searchDroppedResourceValue(
0332: FormProperty property, Object expectedValue) {
0333: if (droppedValues != null) {
0334: String dropKey = getPropertyPath(property, null);
0335: Object value = droppedValues.get(dropKey);
0336: if (value instanceof ResourceValue) {
0337: ResourceValue resValue = (ResourceValue) value;
0338: if (resValue.getValue().equals(expectedValue))
0339: return resValue;
0340: }
0341: }
0342: return null;
0343: }
0344:
0345: /**
0346: * Changes all component's modified properties to resources or i18n. Called
0347: * when a copy of a component is created.
0348: * This method is not called during undo/redo.
0349: */
0350: public static void switchComponentToResources(RADComponent metacomp) {
0351: ResourceSupport support = getResourceSupport(metacomp);
0352: if (support.isAutoMode())
0353: support.switchComponentToResources(metacomp, false, true);
0354: }
0355:
0356: private void switchComponentToResources(RADComponent metacomp,
0357: boolean update, boolean recursive) {
0358: int valueType = PLAIN_VALUE | UNDEFINED_RESOURCE;
0359: for (FormProperty prop : getComponentResourceProperties(
0360: metacomp, valueType, recursive)) {
0361: boolean fire = prop.isChangeFiring(); // suppress firing - avoid usual update when resource is set
0362: prop.setChangeFiring(false);
0363: try {
0364: Object resValue = makeResource0(prop.getValue(), prop);
0365: prop.setValue(resValue);
0366: // raise form version here - FormProperty won't do since firing is off
0367: formModel.raiseVersionLevel(
0368: FormModel.FormVersion.NB60_PRE,
0369: FormModel.FormVersion.NB60);
0370: if (update) {
0371: resValue = prop.getValue(); // makeResource might return ValueWithEditor...
0372: if (resValue instanceof I18nValue) {
0373: getI18nService()
0374: .update(null, (I18nValue) resValue,
0375: getSrcDataObject(),
0376: getI18nBundleName(),
0377: designLocale, true);
0378: } else if (resValue instanceof ResourceValue) {
0379: getResourceService().update(null,
0380: (ResourceValue) resValue,
0381: getSourceFile(), designLocale);
0382: formModel.raiseVersionLevel(
0383: FormModel.FormVersion.NB60,
0384: FormModel.FormVersion.NB60);
0385: }
0386: }
0387: // otherwise update will be triggered by component addition
0388: // (called when copying a component)
0389: } catch (Exception ex) { // getValue, setValue should not fail
0390: ErrorManager.getDefault().notify(
0391: ErrorManager.INFORMATIONAL, ex);
0392: }
0393: prop.setChangeFiring(fire);
0394: }
0395: }
0396:
0397: private static void setupNameProperty(RADComponent metacomp,
0398: boolean set, boolean recursive) {
0399: FormProperty nameProp = getNameProperty(metacomp);
0400: if (nameProp != null) {
0401: try {
0402: if (set && !nameProp.isChanged()) {
0403: nameProp.setValue(metacomp.getName());
0404: } else if (!set
0405: && nameProp.isChanged()
0406: && metacomp.getName().equals(
0407: nameProp.getValue())) { // property value corresponds to the component name
0408: nameProp.restoreDefaultValue();
0409: }
0410: } catch (Exception ex) { // getValue, setValue, restoreValue - should not fail here
0411: ErrorManager.getDefault().notify(
0412: ErrorManager.INFORMATIONAL, ex);
0413: }
0414: }
0415: if (recursive && metacomp instanceof ComponentContainer) {
0416: for (RADComponent subcomp : ((ComponentContainer) metacomp)
0417: .getSubBeans()) {
0418: setupNameProperty(subcomp, set, recursive);
0419: }
0420: }
0421: }
0422:
0423: private void setupNameProperty(boolean set) {
0424: for (RADComponent metacomp : formModel.getAllComponents()) {
0425: setupNameProperty(metacomp, set, false);
0426: }
0427: }
0428:
0429: private static FormProperty getNameProperty(RADComponent metacomp) {
0430: if (metacomp.getBeanInstance() instanceof Component) {
0431: return (FormProperty) metacomp.getPropertyByName("name"); // NOI18N
0432: }
0433: return null;
0434: }
0435:
0436: private void switchFormToResources() {
0437: for (RADComponent metacomp : formModel.getAllComponents()) {
0438: switchComponentToResources(metacomp, true, false);
0439: }
0440:
0441: if (droppedValues != null)
0442: droppedValues.clear();
0443: }
0444:
0445: private void switchFormToPlainValues(String originalBundleName) {
0446: for (RADComponent metacomp : formModel.getAllComponents()) {
0447: for (FormProperty prop : getComponentResourceProperties(
0448: metacomp, VALID_RESOURCE_VALUE, false)) {
0449: Object value = getAutoValue(prop);
0450: if (value != null) {
0451: boolean fire = prop.isChangeFiring();
0452: prop.setChangeFiring(false); // suppress firing and converting
0453: try {
0454: if (value instanceof I18nValue) {
0455: I18nValue i18nValue = (I18nValue) value;
0456: prop
0457: .setValue(new FormProperty.ValueWithEditor(
0458: i18nValue.getValue(),
0459: prop.findDefaultEditor()));
0460: i18nService
0461: .update(
0462: i18nValue,
0463: null,
0464: getSrcDataObject(),
0465: originalBundleName != null ? originalBundleName
0466: : getI18nBundleName(),
0467: null, true);
0468: } else if (value instanceof ResourceValue) {
0469: ResourceValue resValue = (ResourceValue) value;
0470: Object plainValue;
0471: PropertyEditor prEd = prop
0472: .getCurrentEditor();
0473: if (prEd instanceof ResourceWrapperEditor) {
0474: prEd.setValue(value);
0475: plainValue = ((ResourceWrapperEditor) prEd)
0476: .getUnwrappedValue();
0477: } else {
0478: plainValue = resValue.getValue();
0479: }
0480: prop.setValue(plainValue);
0481: resourceService.update(resValue, null,
0482: getSourceFile(), designLocale);
0483: }
0484: addDroppedValue(prop, value); // it will keep data from all locales
0485: // (there is no other way to remember this data for undo)
0486: } catch (Exception ex) {
0487: ErrorManager.getDefault().notify(
0488: ErrorManager.INFORMATIONAL, ex);
0489: }
0490: prop.setChangeFiring(fire);
0491: }
0492: }
0493: }
0494: }
0495:
0496: private void addDroppedValue(FormProperty property, Object value) {
0497: if (droppedValues == null) {
0498: droppedValues = new HashMap<String, Object>();
0499: }
0500: droppedValues.put(getPropertyPath(property, null), value);
0501: }
0502:
0503: /**
0504: * Reacts on component's variable renaming. All automatically created keys
0505: * that use the name of the component will be changed to the new name.
0506: * Also the 'name' property of visual components is updated.
0507: */
0508: public static void componentRenamed(RADComponent metacomp,
0509: String oldName, String newName) {
0510: ResourceSupport support = getResourceSupport(metacomp);
0511: if (support.isAutoMode()) {
0512: support.renameDefaultKeysForComponent(metacomp, null, null,
0513: oldName, newName);
0514: }
0515:
0516: // hack: 'name' property needs special treatment
0517: FormProperty nameProp = getNameProperty(metacomp);
0518: if (nameProp != null && nameProp.isChanged()) {
0519: // boolean fire = nameProp.isChangeFiring();
0520: // nameProp.setChangeFiring(false); // don't want to record this change for undo/redo
0521: try {
0522: Object name = nameProp.getValue();
0523: if (oldName.equals(name) && !name.equals(newName)) {
0524: nameProp.setValue(newName);
0525: }
0526: } catch (Exception ex) { // should not happen
0527: ErrorManager.getDefault().notify(
0528: ErrorManager.INFORMATIONAL, ex);
0529: }
0530: // nameProp.setChangeFiring(fire);
0531: }
0532: }
0533:
0534: public static void formRenamed(FormModel formModel, String oldName) {
0535: ResourceSupport support = FormEditor
0536: .getResourceSupport(formModel);
0537: if (support.getAutoMode() == AUTO_I18N) { // [in other auto modes the keys don't include class name]
0538: support.renameDefaultKeys(oldName);
0539: }
0540: }
0541:
0542: private void renameDefaultKeys(String oldFormName) {
0543: for (RADComponent metacomp : formModel.getAllComponents()) {
0544: renameDefaultKeysForComponent(metacomp, oldFormName,
0545: getSrcDataObject().getName(), null, null);
0546: }
0547: }
0548:
0549: private void renameDefaultKeysForComponent(RADComponent metacomp,
0550: String oldFormName, String newFormName, String oldCompName,
0551: String newCompName) {
0552: if (oldFormName == null) {
0553: oldFormName = getSrcDataObject().getName();
0554: newFormName = oldFormName;
0555: } else {
0556: assert newFormName != null;
0557: }
0558: if (oldCompName == null) {
0559: oldCompName = getComponentName(metacomp);
0560: newCompName = oldCompName;
0561: } else {
0562: assert newCompName != null;
0563: }
0564:
0565: for (FormProperty prop : getComponentResourceProperties(
0566: metacomp, VALID_RESOURCE_VALUE, false)) {
0567: int type = -1;
0568: I18nValue i18nValue = null;
0569: ResourceValue resValue = null;
0570: // check if the value uses a default key
0571: String oldKey = null;
0572: try {
0573: ExternalValue eValue = (ExternalValue) prop.getValue();
0574: oldKey = eValue.getKey();
0575: if (eValue instanceof I18nValue) {
0576: i18nValue = (I18nValue) eValue;
0577: type = AUTO_I18N;
0578: } else if (eValue instanceof ResourceValue) {
0579: resValue = (ResourceValue) eValue;
0580: type = AUTO_RESOURCING;
0581: }
0582: } catch (Exception ex) { // should not happen
0583: ErrorManager.getDefault().notify(
0584: ErrorManager.INFORMATIONAL, ex);
0585: }
0586: String oldDefaultKey = getDefaultKey(oldFormName,
0587: getPropertyPath(prop, oldCompName), type);
0588: if (!isAutoKey(oldKey, oldDefaultKey)) {
0589: continue;
0590: }
0591:
0592: // derive the new key
0593: String suffix = oldKey.length() > oldDefaultKey.length() ? oldKey
0594: .substring(oldDefaultKey.length())
0595: : ""; // NOI18N
0596: String newKey = getDefaultKey(newFormName, getPropertyPath(
0597: prop, newCompName), type)
0598: + suffix;
0599: if (newKey.equals(oldKey)) {
0600: continue;
0601: }
0602:
0603: // set the new key
0604: boolean fire = prop.isChangeFiring();
0605: prop.setChangeFiring(false); // suppress firing - update is not done the usual way
0606: try {
0607: if (i18nValue != null) {
0608: I18nValue oldI18nValue = i18nValue;
0609: I18nValue newI18nValue = i18nService.changeKey(
0610: oldI18nValue, newKey);
0611: prop.setValue(newI18nValue);
0612: i18nService.update(oldI18nValue, newI18nValue,
0613: getSrcDataObject(), getI18nBundleName(),
0614: designLocale, true);
0615: } else if (resValue != null) {
0616: ResourceValue oldResValue = resValue;
0617: ResourceValue newResValue = resourceService
0618: .changeKey(oldResValue, newKey);
0619: prop.setValue(newResValue);
0620: resourceService.update(oldResValue, newResValue,
0621: getSourceFile(), designLocale);
0622: }
0623: } catch (Exception ex) {
0624: ErrorManager.getDefault().notify(
0625: ErrorManager.INFORMATIONAL, ex);
0626: }
0627: prop.setChangeFiring(fire);
0628: }
0629: }
0630:
0631: public static void formMoved(FormModel formModel,
0632: FileObject oldFolder) {
0633: ResourceSupport support = FormEditor
0634: .getResourceSupport(formModel);
0635: if (support.getAutoMode() == AUTO_I18N) { // [in other auto modes the entire properties file is moved]
0636: support.moveFormI18n(oldFolder);
0637: }
0638: }
0639:
0640: private void moveFormI18n(FileObject oldFolder) {
0641: String oldPkg = getPkgResourceName(oldFolder);
0642: String newPkg = getPkgResourceName(getSourceFile());
0643: String newBundle = getI18nBundleName();
0644: String oldBundle = oldPkg
0645: + newBundle.substring(newPkg.length());
0646: switchFormToPlainValues(oldBundle);
0647: switchFormToResources();
0648: // TODO: probably should also copy the manually set values (not just
0649: // move as in case of auto-i18n) - definitely if moving between projects
0650: }
0651:
0652: public static void loadInjectedResources(RADComponent metacomp) {
0653: ResourceSupport support = getResourceSupport(metacomp);
0654: if (support != null && support.getAutoMode() == AUTO_INJECTION)
0655: support.loadInjectedResources0(metacomp);
0656: }
0657:
0658: private void loadInjectedResources0(RADComponent metacomp) {
0659: if (getResourceService() == null)
0660: return;
0661:
0662: String compName = getComponentName(metacomp);
0663: String keyEx = compName + "\\.\\w+"; // NOI18N
0664: Collection<String> compResources = resourceService.findKeys(
0665: keyEx, getSourceFile());
0666: if (compResources.size() > 0) {
0667: String[] propNames = new String[compResources.size()];
0668: int compPrefixLength = compName.length() + 1;
0669: int i = 0;
0670: for (String key : compResources) {
0671: propNames[i++] = key.substring(compPrefixLength);
0672: }
0673: FormProperty[] properties = metacomp
0674: .getBeanProperties(propNames);
0675: i = 0;
0676: for (String key : compResources) {
0677: FormProperty prop = properties[i++];
0678: if (prop != null && isResourceType(prop.getValueType())) {
0679: try {
0680: prop.setValue(resourceService.get(key, prop
0681: .getValueType(), designLocale,
0682: getSourceFile()));
0683: } catch (Exception ex) {
0684: ErrorManager.getDefault().notify(
0685: ErrorManager.INFORMATIONAL, ex);
0686: }
0687: }
0688: }
0689: }
0690: }
0691:
0692: /**
0693: * Reacts on a change in a resourced/internationalized property value.
0694: * Makes sure that added/changed/removed value is updated in the properties
0695: * file. (This method is called only for component properties,
0696: * so it must scan the value for nested properties.)
0697: */
0698: public static void updateStoredValue(Object oldValue,
0699: Object newValue, FormProperty property) {
0700: if (isResourceableProperty(property)) {
0701: ResourceSupport support = getResourceSupport(property);
0702: if (support != null) {
0703: support
0704: .updateStoredValue0(oldValue, newValue,
0705: property);
0706: }
0707: }
0708: }
0709:
0710: private void updateStoredValue0(Object oldValue, Object newValue,
0711: FormProperty property) {
0712: // hack: This method is called whenever a property value changes, so besides
0713: // updating resource values we can also react on changing the "name" property
0714: // which we use to determine the name of automatic resource key.
0715: if (property.getName().equals("name")
0716: && property instanceof RADProperty
0717: && property.getValueType() == String.class) {
0718: RADComponent metacomp = ((RADProperty) property)
0719: .getRADComponent();
0720: String oldName = oldValue instanceof String ? (String) oldValue
0721: : metacomp.getName();
0722: String newName = property.isChanged()
0723: && newValue instanceof String ? (String) newValue
0724: : metacomp.getName();
0725: if (!newName.equals(oldName)) {
0726: componentRenamed(metacomp, oldName, newName);
0727: }
0728: if (!(oldValue instanceof ExternalValue)
0729: && !(newValue instanceof ExternalValue)) {
0730: return;
0731: }
0732: }
0733:
0734: if (isResourceType(property.getValueType())) {
0735: updateStoredValue1(oldValue, newValue, property);
0736: } else {
0737: Collection<FormProperty> colOld = getNestedResourceProperties(
0738: oldValue, property, VALID_RESOURCE_VALUE);
0739: Collection<FormProperty> colNew = getNestedResourceProperties(
0740: newValue, property, VALID_RESOURCE_VALUE);
0741:
0742: for (FormProperty oProp : colOld) {
0743: boolean foundInNew = false;
0744: for (Iterator<FormProperty> it = colNew.iterator(); it
0745: .hasNext();) {
0746: FormProperty nProp = it.next();
0747: if (getPropertyPath(oProp, null).equals(
0748: getPropertyPath(nProp, null))) { // same property
0749: try {
0750: updateStoredValue1(oProp.getValue(), nProp
0751: .getValue(), nProp);
0752: } catch (Exception ex) { // getValue should not fail
0753: ErrorManager.getDefault().notify(
0754: ErrorManager.INFORMATIONAL, ex);
0755: }
0756: it.remove(); // remove so only newly added remain
0757: foundInNew = true;
0758: break;
0759: }
0760: }
0761: if (!foundInNew) { // removed resource/i18n value
0762: try {
0763: updateStoredValue1(oProp.getValue(), null,
0764: oProp);
0765: } catch (Exception ex) { // getValue() should not fail
0766: ErrorManager.getDefault().notify(
0767: ErrorManager.INFORMATIONAL, ex);
0768: }
0769: }
0770: }
0771: // colNew now contains newly added resources/i18n values
0772: for (FormProperty nProp : colNew) {
0773: try {
0774: updateStoredValue1(null, nProp.getValue(), nProp);
0775: } catch (Exception ex) { // getValue should not fail
0776: ErrorManager.getDefault().notify(
0777: ErrorManager.INFORMATIONAL, ex);
0778: }
0779: }
0780: }
0781: }
0782:
0783: private void updateStoredValue1(Object oldValue, Object newValue,
0784: FormProperty property) {
0785: // updateI18nValue and updateResourceValue can each be called once or
0786: // not at all, but not twice
0787: if (oldValue instanceof I18nValue) {
0788: updateI18nValue(oldValue, newValue, property);
0789: if (newValue instanceof ResourceValue)
0790: updateResourceValue(oldValue, newValue, property);
0791: } else if (oldValue instanceof ResourceValue) {
0792: updateResourceValue(oldValue, newValue, property);
0793: if (newValue instanceof I18nValue)
0794: updateI18nValue(oldValue, newValue, property);
0795: }
0796: // updating from plain value
0797: else if (newValue instanceof I18nValue)
0798: updateI18nValue(oldValue, newValue, property);
0799: else if (newValue instanceof ResourceValue)
0800: updateResourceValue(oldValue, newValue, property);
0801: }
0802:
0803: private void updateI18nValue(Object oldValue, Object newValue,
0804: FormProperty property) {
0805: if (getI18nService() != null) {
0806: I18nValue oldVal = oldValue instanceof I18nValue ? (I18nValue) oldValue
0807: : null;
0808: I18nValue newVal = newValue instanceof I18nValue ? (I18nValue) newValue
0809: : null;
0810: try {
0811: i18nService.update(oldVal, newVal, getSrcDataObject(),
0812: getI18nBundleName(), designLocale, isAutoValue(
0813: oldVal, getDefaultKey0(property,
0814: AUTO_I18N)));
0815: } catch (IOException ex) {
0816: // [can't store to properties bundle - should do something?]
0817: ErrorManager.getDefault().notify(
0818: ErrorManager.INFORMATIONAL, ex);
0819: }
0820: }
0821: }
0822:
0823: private void updateResourceValue(Object oldValue, Object newValue,
0824: FormProperty property) {
0825: if (getResourceService() != null) {
0826: ResourceValue oldVal = oldValue instanceof ResourceValue ? (ResourceValue) oldValue
0827: : null;
0828: ResourceValue newVal = newValue instanceof ResourceValue ? (ResourceValue) newValue
0829: : null;
0830: try {
0831: resourceService.update(oldVal, newVal, getSourceFile(),
0832: designLocale);
0833: } catch (IOException ex) {
0834: // [can't store to properties file - should report something?]
0835: ErrorManager.getDefault().notify(
0836: ErrorManager.INFORMATIONAL, ex);
0837: }
0838: }
0839: }
0840:
0841: public static ResourceValue findResource(String key,
0842: FormProperty property) {
0843: return findResource(property.getPropertyContext()
0844: .getFormModel(), key, property.getValueType());
0845: }
0846:
0847: public static ResourceValue findResource(FormModel formModel,
0848: String key, Class valueType) {
0849: return FormEditor.getResourceSupport(formModel).findResource0(
0850: key, valueType);
0851: }
0852:
0853: private ResourceValue findResource0(String key, Class valueType) {
0854: return getResourceService() != null ? resourceService.get(key,
0855: valueType, designLocale, getSourceFile()) : null;
0856: }
0857:
0858: /**
0859: * @return true if the property is capable of holding resource values (i.e. FormDesignValue)
0860: */
0861: public static boolean isResourceableProperty(FormProperty prop) {
0862: return prop.getPropertyContext().useMultipleEditors();
0863: // some layout properties can't accommodate FormDesignValue
0864: }
0865:
0866: /**
0867: * @return true if the property should be resourced if there is a chance to
0868: */
0869: public static boolean isPropertyForResourcing(FormProperty prop) {
0870: if (!isResourceableProperty(prop)) {
0871: return false;
0872: }
0873: ResourceSupport support = getResourceSupport(prop);
0874: return !support.isExcludedProperty0(prop)
0875: && support.isResourceAutoMode();
0876: }
0877:
0878: /**
0879: * Returns whether the given property is excluded from automatic resourcing,
0880: * i.e. marked as it should intentionally hold a plain value. This can
0881: * happen when the user explicitly chooses not to use resource for given
0882: * property value, or there can be properties excluded by default
0883: * (e.g. 'name' property of components).
0884: * NOTE: This method is usaful only for properties that can be resourced,
0885: * it should not be called for properties that can't hold resource values.
0886: * @see isResourceableProperty
0887: * @return true if the property is marked as excluded from automatic
0888: * resourcing (i.e. is expected to hold a plain value)
0889: */
0890: public static boolean isExcludedProperty(FormProperty prop) {
0891: assert isResourceableProperty(prop);
0892: return Boolean.TRUE.equals(prop
0893: .getValue(EXCLUDE_FROM_RESOURCING)) ? true
0894: : getResourceSupport(prop).isExcludedProperty1(prop);
0895: }
0896:
0897: private boolean isExcludedProperty0(FormProperty prop) {
0898: return Boolean.TRUE.equals(prop
0899: .getValue(EXCLUDE_FROM_RESOURCING)) ? true
0900: : isExcludedProperty1(prop);
0901: }
0902:
0903: private boolean isExcludedProperty1(FormProperty prop) {
0904: if (!Boolean.TRUE.equals(prop.getValue(EXCLUSION_DETERMINED))) {
0905: if (getResourceService() == null)
0906: return false;
0907:
0908: prop.setValue(EXCLUSION_DETERMINED, true);
0909: Object propOwner = prop.getPropertyContext().getOwner();
0910: Class type = null;
0911: if (propOwner instanceof RADComponent) {
0912: type = ((RADComponent) propOwner).getBeanClass();
0913: } else if (propOwner instanceof FormProperty) {
0914: type = ((FormProperty) propOwner).getValueType();
0915: }
0916: boolean excl;
0917: if (type != null) {
0918: excl = resourceService.isExcludedProperty(type, prop
0919: .getName());
0920: } else {
0921: excl = false;
0922: }
0923: prop.setValue(EXCLUDE_FROM_RESOURCING, excl);
0924: return excl;
0925: }
0926: return false;
0927: }
0928:
0929: public static void setExcludedProperty(FormProperty prop,
0930: boolean excl) {
0931: if (isResourceableProperty(prop)
0932: && excl != isExcludedProperty(prop)) {
0933: prop.setValue(EXCLUDE_FROM_RESOURCING, excl);
0934: }
0935: }
0936:
0937: static String[] getPropertyAttrNames() {
0938: return PROPERTY_ATTRS;
0939: }
0940:
0941: public static String getInjectionCode(RADComponent metacomp,
0942: String compGenName) {
0943: return getResourceSupport(metacomp).getInjectionCode0(metacomp,
0944: compGenName);
0945: }
0946:
0947: private String getInjectionCode0(RADComponent metacomp,
0948: String compGenName) {
0949: return getAutoMode() == AUTO_INJECTION
0950: && getResourceService() != null ? resourceService
0951: .getInjectionCode(metacomp.getBeanInstance(),
0952: compGenName, getSourceFile()) : null;
0953: }
0954:
0955: public static boolean isInjectedProperty(FormProperty prop) {
0956: if (isResourceableProperty(prop)) {
0957: ResourceSupport support = getResourceSupport(prop);
0958: if (support.getAutoMode() == AUTO_INJECTION) {
0959: Object value;
0960: try {
0961: value = prop.getValue();
0962: if (value instanceof ResourceValue) {
0963: String key = ((ResourceValue) value).getKey();
0964: if (key != null
0965: && support.getDefaultKey0(prop,
0966: AUTO_INJECTION).equals(key)) {
0967: return true;
0968: }
0969: }
0970: } catch (Exception ex) {
0971: ErrorManager.getDefault().notify(
0972: ErrorManager.INFORMATIONAL, ex);
0973: }
0974: }
0975: }
0976: return false;
0977: }
0978:
0979: public boolean isDefaultInternationalizableProject() {
0980: if (getI18nService() != null)
0981: return i18nService
0982: .isDefaultInternationalizableProject(getSourceFile());
0983: else
0984: return false;
0985: }
0986:
0987: public boolean projectUsesResources() {
0988: if (getResourceService() != null)
0989: return resourceService
0990: .projectUsesResources(getSourceFile());
0991: else
0992: return false;
0993: }
0994:
0995: public boolean projectWantsUseResources() {
0996: if (getResourceService() != null)
0997: return resourceService
0998: .projectWantsUseResources(getSourceFile());
0999: else
1000: return false;
1001: }
1002:
1003: private void bundleChanged(String oldBundle) {
1004: switchFormToPlainValues(oldBundle);
1005: String oldLocale = designLocale;
1006: setDesignLocale(""); // NOI18N
1007: FormEditor.getFormEditor(formModel).getFormRootNode()
1008: .firePropertyChangeHelper(PROP_DESIGN_LOCALE,
1009: oldLocale, designLocale);
1010: switchFormToResources();
1011: }
1012:
1013: String getDesignLocale() {
1014: return designLocale;
1015: }
1016:
1017: private void changeDesignLocale(String designLocale) {
1018: setDesignLocale(designLocale);
1019: updateDesignLocale();
1020: // hack to update designer...
1021: formModel.fireEvents((FormModelEvent[]) null);
1022: }
1023:
1024: private void setDesignLocale(String locale) {
1025: designLocale = locale;
1026: rememberedLocales.put(getSrcDataObject(), locale); // keep to survive form reload
1027: }
1028:
1029: private void updateDesignLocale() {
1030: Collection<FormProperty> props = getAllResourceProperties(VALID_RESOURCE_VALUE);
1031: // read all values in advance - setting certain properties might reset others (e.g. action and text)
1032: List<Object> values = new ArrayList<Object>(props.size());
1033: for (FormProperty prop : props) {
1034: try {
1035: values.add(prop.getValue());
1036: } catch (Exception ex) {
1037: ErrorManager.getDefault().notify(
1038: ErrorManager.INFORMATIONAL, ex);
1039: }
1040: }
1041: // change locale of the resource values and set them back
1042: Iterator it = values.iterator();
1043: for (FormProperty prop : props) {
1044: Object value = it.next();
1045: boolean fire = prop.isChangeFiring();
1046: prop.setChangeFiring(false);
1047: try {
1048: if (value instanceof I18nValue) {
1049: prop.setValue(i18nService.switchLocale(
1050: (I18nValue) value, designLocale));
1051: } else if (value instanceof ResourceValue) {
1052: prop.setValue(resourceService.switchLocale(
1053: (ResourceValue) value, designLocale));
1054: }
1055: } catch (Exception ex) {
1056: ErrorManager.getDefault().notify(
1057: ErrorManager.INFORMATIONAL, ex);
1058: }
1059: prop.setChangeFiring(fire);
1060: }
1061: }
1062:
1063: public static ResourcePanel createResourcePanel(
1064: FormProperty property, boolean force) {
1065: if (!isResourceableProperty(property))
1066: return null;
1067:
1068: return getResourceSupport(property).createResourcePanel0(
1069: property, force);
1070: }
1071:
1072: private ResourcePanel createResourcePanel0(FormProperty prop,
1073: boolean force) {
1074: if (getResourceService() != null
1075: && (force || (!isI18nAutoMode()
1076: && (isResourceAutoMode() || projectUsesResources()) && !isAutoNamedProperty0(prop)))) {
1077: return resourceService.createResourcePanel(prop
1078: .getValueType(), getSourceFile());
1079: } else
1080: return null;
1081: }
1082:
1083: private static boolean isAutoNamedProperty(FormProperty prop) {
1084: return "name".equals(prop.getName()) // NOI18N
1085: && prop instanceof RADProperty
1086: && getNameProperty(((RADProperty) prop)
1087: .getRADComponent()) == prop
1088: && getResourceSupport(prop).isAutoName();
1089: }
1090:
1091: private boolean isAutoNamedProperty0(FormProperty prop) {
1092: return isAutoName()
1093: && "name".equals(prop.getName()) // NOI18N
1094: && prop instanceof RADProperty
1095: && getNameProperty(((RADProperty) prop)
1096: .getRADComponent()) == prop;
1097: }
1098:
1099: /**
1100: * @param formModel
1101: * @return Resource files to be affected by a change inside the form (e.g.
1102: * by renaming a component).
1103: */
1104: public static List<URL> getFilesForContentChangeBackup(
1105: FormModel formModel) {
1106: return FormEditor.getResourceSupport(formModel)
1107: .getFilesForContentChangeBackup();
1108: }
1109:
1110: private List<URL> getFilesForContentChangeBackup() {
1111: // with content change we can backup all i18n and resource files
1112: if (isI18nAutoMode()) {
1113: return getI18nService().getResourceFiles(getSourceFile(),
1114: getI18nBundleName());
1115: } else if (isResourceAutoMode()) {
1116: return getResourceService().getResourceFiles(
1117: getSourceFile());
1118: } else {
1119: return Collections.emptyList();
1120: }
1121: }
1122:
1123: /**
1124: * @param formModel
1125: * @return Resource files to be affected by renaming a form class.
1126: */
1127: public static List<URL> getFilesForFormRenameBackup(
1128: FormModel formModel) {
1129: return FormEditor.getResourceSupport(formModel)
1130: .getI18nFilesForFormRenameBackup();
1131: }
1132:
1133: private List<URL> getI18nFilesForFormRenameBackup() {
1134: // With form rename we only backup i18n properties files.
1135: // App framework properties files are renamed by separate refactoring,
1136: // need not be backed up (as not changed inside).
1137: if (isI18nAutoMode()) {
1138: return getI18nService().getResourceFiles(getSourceFile(),
1139: getI18nBundleName());
1140: } else {
1141: return Collections.emptyList();
1142: }
1143: }
1144:
1145: /**
1146: * @param formModel
1147: * @param oldFolder
1148: * @return Resource files to be affected by moving the form. URL of an
1149: * non-existing file indicates the file will be created.
1150: */
1151: public static List<URL> getFilesForFormMoveBackup(
1152: FormModel formModel, FileObject oldFolder) {
1153: return FormEditor.getResourceSupport(formModel)
1154: .getFilesForFormMoveBackup(oldFolder);
1155: }
1156:
1157: private List<URL> getFilesForFormMoveBackup(FileObject oldFolder) {
1158: // With form move we only backup i18n properties files - the current ones.
1159: // A new one may be created in the new package - may not exist yet.
1160: // App framework properties files are moved by separate refactoring,
1161: // need not be backed up (as not changed inside).
1162: if (isI18nAutoMode()) {
1163: String oldPkg = getPkgResourceName(oldFolder);
1164: String newPkg = getPkgResourceName(getSourceFile());
1165: String newBundle = getI18nBundleName();
1166: String oldBundle = oldPkg
1167: + newBundle.substring(newPkg.length());
1168: List<URL> oldFiles = getI18nService().getResourceFiles(
1169: getSourceFile(), oldBundle);
1170: List<URL> newFiles = getI18nService().getResourceFiles(
1171: getSourceFile(), newBundle);
1172: List<URL> all = new ArrayList<URL>(oldFiles.size()
1173: + newFiles.size());
1174: all.addAll(oldFiles);
1175: all.addAll(newFiles);
1176: return all;
1177: } else {
1178: return Collections.emptyList();
1179: }
1180: }
1181:
1182: // -----
1183: // listener on FormModel - reacts on component additions/removals
1184:
1185: private class ModelListener implements FormModelListener {
1186: public void formChanged(FormModelEvent[] events) {
1187: if (events == null) {
1188: return;
1189: }
1190: getI18nService();
1191: getResourceService();
1192: if (i18nService == null && resourceService == null) {
1193: return;
1194: }
1195:
1196: for (int i = 0; i < events.length; i++) {
1197: FormModelEvent ev = events[i];
1198: switch (ev.getChangeType()) {
1199:
1200: case FormModelEvent.COMPONENT_REMOVED:
1201: if (ev.getCreatedDeleted()) {
1202: for (FormProperty prop : getComponentResourceProperties(
1203: ev.getComponent(),
1204: VALID_RESOURCE_VALUE, true)) {
1205: Object value = getAutoValue(prop);
1206: if (value != null) {
1207: // let's remove this key from properties file
1208: try {
1209: if (value instanceof I18nValue
1210: && i18nService != null) {
1211: i18nService.update(
1212: (I18nValue) value,
1213: null,
1214: getSrcDataObject(),
1215: getI18nBundleName(),
1216: null, true);
1217: } else if (value instanceof ResourceValue
1218: && resourceService != null) {
1219: resourceService.update(
1220: (ResourceValue) value,
1221: null, getSourceFile(),
1222: null);
1223: }
1224: } catch (IOException ex) {
1225: ErrorManager.getDefault().notify(
1226: ErrorManager.INFORMATIONAL,
1227: ex);
1228: }
1229: }
1230: }
1231: }
1232: break;
1233:
1234: case FormModelEvent.COMPONENT_ADDED:
1235: if (ev.getCreatedDeleted()) {
1236: RADComponent addedComp = ev.getComponent();
1237: for (FormProperty prop : getComponentResourceProperties(
1238: addedComp, VALID_RESOURCE_VALUE, true)) {
1239: try { // add resource/i18n value to properties file (restore)
1240: Object value = prop.getValue();
1241: if (value instanceof I18nValue
1242: && i18nService != null) {
1243: i18nService.update(null,
1244: (I18nValue) value,
1245: getSrcDataObject(),
1246: getI18nBundleName(),
1247: designLocale, false);
1248: } else if (value instanceof ResourceValue
1249: && resourceService != null) {
1250: resourceService.update(null,
1251: (ResourceValue) value,
1252: getSourceFile(),
1253: designLocale);
1254: }
1255: } catch (Exception ex) {
1256: ErrorManager.getDefault().notify(
1257: ErrorManager.INFORMATIONAL, ex);
1258: }
1259: }
1260: if (isAutoName()
1261: && addedComp != formModel
1262: .getTopRADComponent()
1263: && formModel.isUndoRedoRecording()) { // (don't set name to components during undo/redo)
1264: setupNameProperty(addedComp, true, true);
1265: }
1266: }
1267: break;
1268:
1269: case FormModelEvent.FORM_TO_BE_SAVED:
1270: if (i18nService != null)
1271: i18nService.autoSave(getSrcDataObject());
1272: if (resourceService != null)
1273: resourceService.autoSave(getSourceFile());
1274: break;
1275:
1276: case FormModelEvent.FORM_TO_BE_CLOSED:
1277: if (i18nService != null)
1278: i18nService.close(getSrcDataObject());
1279: if (resourceService != null)
1280: resourceService.close(getSourceFile());
1281: break;
1282: }
1283: }
1284: }
1285: }
1286:
1287: // -----
1288:
1289: private Collection<FormProperty> getAllResourceProperties(
1290: int valueType) {
1291: Collection<RADComponent> components = formModel
1292: .getAllComponents();
1293: List<FormProperty> propList = new ArrayList<FormProperty>(
1294: components.size());
1295: for (RADComponent metacomp : components) {
1296: collectResourceProperties(metacomp, valueType, false,
1297: propList);
1298: }
1299: return propList;
1300: }
1301:
1302: private Collection<FormProperty> getComponentResourceProperties(
1303: RADComponent metacomp, int valueType, boolean recursive) {
1304: Collection<FormProperty> col = collectResourceProperties(
1305: metacomp, valueType, recursive, null);
1306: if (col == null)
1307: col = Collections.emptyList();
1308: return col;
1309: }
1310:
1311: private Collection<FormProperty> getNestedResourceProperties(
1312: Object value, FormProperty prop, int valueType) {
1313: Collection<FormProperty> col = collectNestedResourceProperties(
1314: value, prop, valueType, null);
1315: if (col == null)
1316: col = Collections.emptyList();
1317: return col;
1318: }
1319:
1320: private Collection<FormProperty> collectResourceProperties(
1321: RADComponent metacomp, int valueType, boolean recursive,
1322: Collection<FormProperty> col) {
1323: // check bean properties
1324: for (FormProperty prop : metacomp.getKnownBeanProperties()) {
1325: if (prop.isChanged() && isResourceableProperty(prop)
1326: && !isExcludedProperty0(prop))
1327: col = collectNestedResourceProperties(prop, valueType,
1328: col);
1329: }
1330:
1331: // check layout constraints
1332: if (metacomp instanceof RADVisualComponent) {
1333: Node.Property[] constrProps = ((RADVisualComponent) metacomp)
1334: .getConstraintsProperties();
1335: if (constrProps != null) {
1336: for (Node.Property p : constrProps) {
1337: if (p instanceof FormProperty) {
1338: FormProperty prop = (FormProperty) p;
1339: if (prop.isChanged()
1340: && isResourceableProperty(prop)
1341: && !isExcludedProperty0(prop))
1342: col = collectNestedResourceProperties(prop,
1343: valueType, col);
1344: }
1345: }
1346: }
1347: }
1348:
1349: // check subcomponents
1350: if (recursive && metacomp instanceof ComponentContainer) {
1351: for (RADComponent subcomp : ((ComponentContainer) metacomp)
1352: .getSubBeans()) {
1353: col = collectResourceProperties(subcomp, valueType,
1354: recursive, col);
1355: }
1356: }
1357:
1358: return col;
1359: }
1360:
1361: private Collection<FormProperty> collectNestedResourceProperties(
1362: Object value, FormProperty property, int valueType,
1363: Collection<FormProperty> col) {
1364: Node.Property[] nestedProps = getNestedProperties(value);
1365:
1366: if (nestedProps == null) { // leaf property, no more nesting
1367: if ((valueType != PLAIN_VALUE || isResourceType(property
1368: .getValueType()))
1369: && isWanted(valueType, value)) { // for plain value the property type must match one of the supported types;
1370: // for resource value only the key is checked
1371: if (col == null)
1372: col = new LinkedList<FormProperty>();
1373: col.add(property);
1374: }
1375: return col;
1376: }
1377:
1378: for (Node.Property p : nestedProps) {
1379: if (p instanceof FormProperty) {
1380: FormProperty prop = (FormProperty) p;
1381: if (prop.isChanged() && isResourceableProperty(prop)
1382: && !isExcludedProperty0(prop))
1383: col = collectNestedResourceProperties(prop,
1384: valueType, col);
1385: }
1386: }
1387: return col;
1388: }
1389:
1390: private Collection<FormProperty> collectNestedResourceProperties(
1391: FormProperty property, int valueType,
1392: Collection<FormProperty> col) {
1393: try {
1394: return collectNestedResourceProperties(property.getValue(),
1395: property, valueType, col);
1396: } catch (Exception ex) {
1397: ErrorManager.getDefault().notify(
1398: ErrorManager.INFORMATIONAL, ex);
1399: }
1400: return col;
1401: }
1402:
1403: private static Node.Property[] getNestedProperties(Object value) {
1404: if (value instanceof BorderDesignSupport) {
1405: return ((BorderDesignSupport) value).getProperties();
1406: } else if (value instanceof TableColumnModelEditor.FormTableColumnModel) {
1407: TableColumnModelEditor.FormTableColumnModel columnModel = (TableColumnModelEditor.FormTableColumnModel) value;
1408: List<Node.Property> props = new ArrayList<Node.Property>(
1409: columnModel.getColumns().size());
1410: for (TableColumnModelEditor.FormTableColumn column : columnModel
1411: .getColumns()) {
1412: props.add(column.getTitle());
1413: }
1414: return props.toArray(new Node.Property[props.size()]);
1415: }
1416: // [An alternative would be to use BeanPropertyEditor, but calling
1417: // getCurrentEditor() forces searching for the PropertyEditor.
1418: // Maybe not a problem because "known" properties should have it
1419: // already, or will have to find it sooner or later.]
1420: return null;
1421: }
1422:
1423: private static boolean isWanted(int valueType, Object value) {
1424: String key;
1425: if (value instanceof ExternalValue) {
1426: key = ((ExternalValue) value).getKey();
1427: if (key == null) { // it is a complex resource value without a single key
1428: key = ""; // NOI18N
1429: }
1430: } else { // it is a plain value
1431: key = null;
1432: }
1433:
1434: if (key == null) {
1435: return (valueType & PLAIN_VALUE) != 0
1436: && !(value instanceof FormDesignValue);
1437: }
1438:
1439: if ((valueType & UNDEFINED_RESOURCE) != 0
1440: && key.equals(ExternalValue.COMPUTE_AUTO_KEY)) {
1441: return true;
1442: }
1443:
1444: if ((valueType & VALID_RESOURCE_VALUE) != 0
1445: && !key.startsWith("#")) { // NOI18N
1446: return true;
1447: }
1448:
1449: return false;
1450: }
1451:
1452: // -----
1453:
1454: private String getComponentName(RADComponent metacomp) {
1455: String name = null;
1456: boolean rootName = false;
1457: if (isResourceAutoMode()) {
1458: FormProperty nameProp = getNameProperty(metacomp);
1459: // if 'name' property is set, we use its value as the name of the component
1460: if (nameProp != null && nameProp.isChanged()) {
1461: try {
1462: name = (String) nameProp.getValue();
1463: } catch (Exception ex) { // should not fail, ignore non-strings
1464: }
1465: }
1466: rootName = true;
1467: }
1468: if ((metacomp != formModel.getTopRADComponent() || rootName)
1469: && (name == null || name.trim().length() == 0)) { // TBD some check for usable name
1470: // by default use the name of the variable
1471: name = metacomp.getName();
1472: }
1473: return name;
1474: }
1475:
1476: private String getPropertyPath(FormProperty property,
1477: String compName) {
1478: String propertyName = property.getName();
1479: List<Object> parents = new ArrayList<Object>();
1480: do {
1481: FormPropertyContext propContext = property
1482: .getPropertyContext();
1483: Object parent = propContext.getOwner();
1484: if (parent instanceof FormProperty) {
1485: parents.add(parent);
1486: property = (FormProperty) parent;
1487: } else {
1488: if (parent instanceof RADComponent) {
1489: parents.add(parent);
1490: }
1491: property = null;
1492: }
1493: } while (property != null);
1494:
1495: StringBuilder buf = new StringBuilder();
1496: for (ListIterator it = parents.listIterator(parents.size()); it
1497: .hasPrevious();) {
1498: Object parent = it.previous();
1499: String append;
1500: if (parent instanceof RADComponent) {
1501: append = compName != null ? compName
1502: : getComponentName((RADComponent) parent);
1503: } else {
1504: append = ((FormProperty) parent).getName();
1505: }
1506: if (append != null) {
1507: if (buf.length() > 0) {
1508: buf.append("."); // NOI18N
1509: }
1510: buf.append(append);
1511: }
1512: }
1513: if (buf.length() > 0) {
1514: buf.append("."); // NOI18N
1515: }
1516: buf.append(propertyName);
1517: return buf.toString();
1518: }
1519:
1520: private String getDefaultKey0(FormProperty prop, int type) {
1521: return getDefaultKey(getSrcDataObject().getName(),
1522: getPropertyPath(prop, null), type);
1523: }
1524:
1525: static String getDefaultKey(FormProperty prop, int type) {
1526: return getResourceSupport(prop).getDefaultKey0(prop, type);
1527: }
1528:
1529: private String getDefaultKey(String formName,
1530: String propPath/*, String propName*/, int type) {
1531: if (type != AUTO_I18N) {
1532: // TBD consult the I18nService for the default key...
1533: // TBD consult the ResourceService for the default key...
1534: formName = null;
1535: }
1536: StringBuilder buf = new StringBuilder();
1537: if (formName != null) {
1538: buf.append(formName).append("."); // NOI18N
1539: }
1540: buf.append(propPath);
1541: return buf.toString();
1542: }
1543:
1544: private String getDefaultKey(FormProperty prop, ExternalValue eValue) {
1545: if (eValue instanceof I18nValue) {
1546: return getDefaultKey0(prop, AUTO_I18N);
1547: } else if (eValue instanceof ResourceValue) {
1548: return getDefaultKey0(prop, AUTO_RESOURCING);
1549: } else {
1550: return null;
1551: }
1552: }
1553:
1554: private FileObject getSourceFile() {
1555: DataObject dobj = FormEditor.getFormDataObject(formModel);
1556: return dobj != null ? dobj.getPrimaryFile() : null;
1557: }
1558:
1559: private DataObject getSrcDataObject() {
1560: return FormEditor.getFormDataObject(formModel);
1561: }
1562:
1563: private int getAutoMode() {
1564: return formModel.getSettings().getResourceAutoMode();
1565: }
1566:
1567: private boolean isAutoMode() {
1568: return getAutoMode() != AUTO_OFF;
1569: }
1570:
1571: private boolean isResourceAutoMode() {
1572: int mode = formModel.getSettings().getResourceAutoMode();
1573: return getResourceService() != null
1574: && (mode == AUTO_RESOURCING || mode == AUTO_INJECTION);
1575: }
1576:
1577: private boolean isI18nAutoMode() {
1578: return getI18nService() != null
1579: && formModel.getSettings().getResourceAutoMode() == AUTO_I18N;
1580: }
1581:
1582: private boolean isAutoName() {
1583: return formModel.getSettings().getAutoSetComponentName(); // || getAutoMode() == AUTO_INJECTION;
1584: }
1585:
1586: private String getI18nBundleName() {
1587: String bundleName = formModel.getSettings().getFormBundle();
1588: if (bundleName == null) {
1589: if (defaultI18nBundle == null) {
1590: FileObject file = getSourceFile();
1591: defaultI18nBundle = composeBundleName(
1592: getPkgResourceName(getSourceFile()),
1593: DEFAULT_BUNDLE_NAME);
1594: // [we could also search for another properties file in the package
1595: // (what if there is properties file not for i18n?),
1596: // or try to remember last specified properties file for another form in this package]
1597: }
1598: bundleName = defaultI18nBundle;
1599: }
1600: return bundleName;
1601: }
1602:
1603: private static String getPkgResourceName(FileObject fo) {
1604: ClassPath cp = ClassPath.getClassPath(fo, ClassPath.SOURCE);
1605: if (cp != null) {
1606: return cp.getResourceName(fo.isFolder() ? fo : fo
1607: .getParent());
1608: } else {
1609: return null;
1610: }
1611: }
1612:
1613: private static String composeBundleName(String pkgResName,
1614: String bundleSimpleName) {
1615: return pkgResName != null && pkgResName.length() > 0 ? pkgResName
1616: + "/" + bundleSimpleName
1617: : bundleSimpleName; // NOI18N
1618: }
1619:
1620: private static boolean isResourceType(Class type) {
1621: return type == String.class
1622: || java.awt.Font.class.isAssignableFrom(type)
1623: || javax.swing.Icon.class.isAssignableFrom(type)
1624: || java.awt.Color.class.isAssignableFrom(type);
1625: }
1626:
1627: private static boolean isConvertibleToResource(Object value) {
1628: return value instanceof String
1629: || value instanceof java.awt.Font
1630: || value instanceof org.netbeans.modules.form.editors.IconEditor.NbImageIcon
1631: || value instanceof java.awt.Color;
1632: }
1633:
1634: /**
1635: * Returns value of the property if it is an ExternalValue with a default
1636: * key (i.e. used exclusively only in this property).
1637: */
1638: private ExternalValue getAutoValue(FormProperty prop) {
1639: Object value;
1640: try {
1641: value = prop.getValue();
1642: } catch (Exception ex) {
1643: ErrorManager.getDefault().notify(
1644: ErrorManager.INFORMATIONAL, ex);
1645: return null;
1646: }
1647: ExternalValue eVal;
1648: if (value instanceof ExternalValue) {
1649: eVal = (ExternalValue) value;
1650: if (isAutoValue(eVal, getDefaultKey(prop, eVal))) {
1651: return eVal;
1652: }
1653: }
1654: return null;
1655: }
1656:
1657: private static boolean isAutoValue(ExternalValue value,
1658: String defaultKey) {
1659: String key = (value != null) ? value.getKey() : null;
1660: return key != null ? isAutoKey(key, defaultKey) : false;
1661: }
1662:
1663: private static boolean isAutoKey(String key, String defaultKey) {
1664: return key != null && key.startsWith(defaultKey);
1665: }
1666:
1667: private static String getStringValue(FormProperty prop, Object value) {
1668: if (value instanceof String)
1669: return (String) value;
1670:
1671: PropertyEditor prEd = prop.getCurrentEditor();
1672: prEd.setValue(value);
1673: return prEd.getAsText(); // [this does not work correctly with IconEditor...]
1674: }
1675:
1676: // -----
1677:
1678: Node.Property[] createFormProperties() {
1679: Node.Property autoNamingProp = new PropertySupport.ReadWrite<Boolean>(
1680: FormLoaderSettings.PROP_AUTO_SET_COMPONENT_NAME,
1681: Boolean.TYPE,
1682: FormUtils
1683: .getBundleString("PROP_AUTO_SET_COMPONENT_NAME"), // NOI18N
1684: FormUtils
1685: .getBundleString("HINT_AUTO_SET_COMPONENT_NAME")) // NOI18N
1686: {
1687: public void setValue(Boolean value) {
1688: Boolean oldValue = getValue();
1689: if (!oldValue.equals(value)) {
1690: boolean autoName = value.booleanValue();
1691: formModel.getSettings().setAutoSetComponentName(
1692: autoName);
1693:
1694: setupNameProperty(autoName);
1695:
1696: formModel.fireSyntheticPropertyChanged(null,
1697: PROP_AUTO_SET_COMPONENT_NAME, oldValue,
1698: value);
1699: FormEditor.getFormEditor(formModel)
1700: .getFormRootNode()
1701: .firePropertyChangeHelper(
1702: PROP_AUTO_SET_COMPONENT_NAME,
1703: oldValue, value);
1704: }
1705: }
1706:
1707: public Boolean getValue() {
1708: return Boolean.valueOf(formModel.getSettings()
1709: .getAutoSetComponentName());
1710: }
1711: };
1712:
1713: Node.Property autoModeProp;
1714: int mode = getAutoMode();
1715: if (projectUsesResources() || mode == AUTO_RESOURCING
1716: || mode == AUTO_INJECTION) {
1717: autoModeProp = new PropertySupport.ReadWrite<Integer>(
1718: PROP_AUTO_RESOURCING, Integer.TYPE,
1719: FormUtils.getBundleString("PROP_AUTO_RESOURCE"), // NOI18N
1720: FormUtils
1721: .getBundleString("HINT_AUTO_RESOURCE_LOCAL")) // NOI18N
1722: {
1723: public void setValue(Integer value) {
1724: int oldMode = getAutoMode();
1725: if (value == null || value.equals(oldMode)) {
1726: return;
1727: }
1728: int newMode = value.intValue();
1729: FormSettings settings = formModel.getSettings();
1730: boolean i18nResChange // changing between i18n and resourcing?
1731: = (oldMode == AUTO_I18N && (newMode == AUTO_RESOURCING || newMode == AUTO_INJECTION))
1732: || (newMode == AUTO_I18N && (oldMode == AUTO_RESOURCING || oldMode == AUTO_INJECTION));
1733: // don't change components if only swapping AUTO_RESOURCING with AUTO_INJECTION
1734: if (newMode == AUTO_OFF || i18nResChange) {
1735: switchFormToPlainValues(null);
1736: }
1737: settings.setResourceAutoMode(newMode);
1738: if (oldMode == AUTO_OFF || i18nResChange) {
1739: switchFormToResources();
1740: }
1741: if (newMode == AUTO_INJECTION && !isAutoName()) {
1742: formModel.getSettings()
1743: .setAutoSetComponentName(true);
1744: setupNameProperty(true);
1745: FormEditor.getFormEditor(formModel)
1746: .getFormRootNode()
1747: .firePropertyChangeHelper(
1748: PROP_AUTO_SET_COMPONENT_NAME,
1749: oldMode, newMode);
1750: }
1751:
1752: formModel.fireSyntheticPropertyChanged(null,
1753: PROP_AUTO_RESOURCING, oldMode, newMode);
1754: FormEditor.getFormEditor(formModel)
1755: .getFormRootNode()
1756: .firePropertyChangeHelper(
1757: PROP_AUTO_RESOURCING, oldMode,
1758: newMode);
1759: }
1760:
1761: public Integer getValue() {
1762: return getAutoMode();
1763: }
1764:
1765: @Override
1766: public PropertyEditor getPropertyEditor() {
1767: return new org.netbeans.modules.form.editors.EnumEditor(
1768: new Object[] {
1769: FormUtils
1770: .getBundleString("CTL_AUTO_OFF"),
1771: AUTO_OFF,
1772: "", // NOI18N
1773: FormUtils
1774: .getBundleString("CTL_AUTO_I18N"),
1775: AUTO_I18N,
1776: "", // NOI18N
1777: FormUtils
1778: .getBundleString("CTL_AUTO_RESOURCING"),
1779: AUTO_RESOURCING,
1780: "", // NOI18N
1781: FormUtils
1782: .getBundleString("CTL_AUTO_INJECTION"),
1783: AUTO_INJECTION, "" // NOI18N
1784: });
1785: }
1786: };
1787: } else { // only offer automatic internationalization
1788: autoModeProp = new PropertySupport.ReadWrite<Boolean>(
1789: PROP_AUTO_RESOURCING, Boolean.TYPE, FormUtils
1790: .getBundleString("PROP_AUTO_I18N"), // NOI18N
1791: FormUtils.getBundleString("HINT_AUTO_I18N")) // NOI18N
1792: {
1793: public void setValue(Boolean value) {
1794: boolean oldAutoI18n = getAutoMode() == AUTO_I18N;
1795: Boolean oldValue = Boolean.valueOf(oldAutoI18n);
1796: if (!oldValue.equals(value)) {
1797: boolean newAutoI18n = value.booleanValue();
1798: FormSettings settings = formModel.getSettings();
1799: // set the setting itself so it is "on" during processing the
1800: // resource values (to correctly determine names of components
1801: // for default keys)
1802: if (newAutoI18n) {
1803: settings.setResourceAutoMode(AUTO_I18N);
1804: switchFormToResources();
1805: } else {
1806: switchFormToPlainValues(null);
1807: settings.setResourceAutoMode(AUTO_OFF);
1808: }
1809:
1810: formModel.fireSyntheticPropertyChanged(null,
1811: PROP_AUTO_RESOURCING, oldValue, value);
1812: FormEditor.getFormEditor(formModel)
1813: .getFormRootNode()
1814: .firePropertyChangeHelper(
1815: PROP_AUTO_RESOURCING, oldValue,
1816: value);
1817: }
1818: }
1819:
1820: public Boolean getValue() {
1821: return getAutoMode() == AUTO_I18N;
1822: }
1823: };
1824: }
1825:
1826: Node.Property formBundleProp = new PropertySupport.ReadWrite<String>(
1827: PROP_FORM_BUNDLE, String.class, FormUtils
1828: .getBundleString("PROP_FORM_BUNDLE"), // NOI18N
1829: FormUtils.getBundleString("HINT_FORM_BUNDLE")) // NOI18N
1830: {
1831: public void setValue(String value) {
1832: String oldValue = getI18nBundleName();
1833: if ((oldValue == null && value != null)
1834: || !oldValue.equals(value)) {
1835: String resourceName = value;
1836: if (resourceName != null
1837: && resourceName.toLowerCase().endsWith(
1838: ".properties")) { // NOI18N
1839: resourceName = resourceName.substring(0,
1840: resourceName.length()
1841: - ".properties".length()); // NOI18N
1842: }
1843: formModel.getSettings().setFormBundle(resourceName);
1844: bundleChanged(oldValue);
1845: formModel.fireSyntheticPropertyChanged(null,
1846: PROP_FORM_BUNDLE, oldValue, value);
1847: FormEditor.getFormEditor(formModel)
1848: .getFormRootNode()
1849: .firePropertyChangeHelper(PROP_FORM_BUNDLE,
1850: oldValue, value);
1851: }
1852: }
1853:
1854: public String getValue() {
1855: return getI18nBundleName();
1856: }
1857:
1858: @Override
1859: public PropertyEditor getPropertyEditor() {
1860: return new BundleFilePropertyEditor();
1861: }
1862: };
1863:
1864: Node.Property localeProp = new PropertySupport.ReadWrite<String>(
1865: PROP_DESIGN_LOCALE, String.class, FormUtils
1866: .getBundleString("PROP_DESIGN_LOCALE"), // NOI18N
1867: FormUtils.getBundleString("HINT_DESIGN_LOCALE")) // NOI18N
1868: {
1869: public void setValue(String value) {
1870: // this property is not persistent (not stored in .form file)
1871: String oldValue = designLocale;
1872: changeDesignLocale(value);
1873: formModel.fireSyntheticPropertyChanged(null,
1874: PROP_DESIGN_LOCALE, oldValue, value);
1875: FormEditor.getFormEditor(formModel).getFormRootNode()
1876: .firePropertyChangeHelper(PROP_DESIGN_LOCALE,
1877: oldValue, value);
1878: }
1879:
1880: public String getValue() {
1881: return designLocale;
1882: }
1883:
1884: @Override
1885: public PropertyEditor getPropertyEditor() {
1886: return new LocalePropertyEditor();
1887: }
1888: };
1889:
1890: int autoMode = getAutoMode();
1891: return autoMode == AUTO_OFF || autoMode == AUTO_I18N ? new Node.Property[] {
1892: autoNamingProp, autoModeProp, formBundleProp,
1893: localeProp }
1894: : new Node.Property[] { autoNamingProp, autoModeProp,
1895: localeProp };
1896: }
1897:
1898: private class BundleFilePropertyEditor extends
1899: PropertyEditorSupport {
1900: @Override
1901: public boolean supportsCustomEditor() {
1902: return getI18nService() != null;
1903: }
1904:
1905: @Override
1906: public Component getCustomEditor() {
1907: return getI18nService() != null ? i18nService
1908: .getBundleSelectionComponent(this , getSourceFile())
1909: : null;
1910: }
1911: }
1912:
1913: private class LocalePropertyEditor extends PropertyEditorSupport {
1914: private String[][] tags;
1915:
1916: @Override
1917: public String[] getTags() {
1918: if (tags == null) {
1919: FileObject srcFile = getSourceFile();
1920: if (srcFile != null) { // might be called even if the form is already closed
1921: if (isI18nAutoMode())
1922: tags = i18nService.getAvailableLocales(srcFile,
1923: getI18nBundleName());
1924: else if (isResourceAutoMode())
1925: tags = resourceService
1926: .getAvailableLocales(srcFile);
1927: }
1928: }
1929: return tags != null ? tags[1] : null;
1930: }
1931:
1932: @Override
1933: public void setAsText(String text) {
1934: getTags();
1935: if (tags != null) {
1936: for (int i = 0, n = tags[0].length; i < n; i++) {
1937: if (tags[1][i].equals(text)) {
1938: setValue(tags[0][i]);
1939: return;
1940: }
1941: }
1942: }
1943: setValue(text);
1944: }
1945:
1946: @Override
1947: public String getAsText() {
1948: Object value = getValue();
1949: getTags();
1950: if (tags != null) {
1951: for (int i = 0, n = tags[0].length; i < n; i++) {
1952: if (tags[0][i].equals(value))
1953: return tags[1][i];
1954: }
1955: }
1956: return value != null ? value.toString() : null;
1957: }
1958:
1959: @Override
1960: public boolean supportsCustomEditor() {
1961: return getTags() != null;
1962: }
1963:
1964: @Override
1965: public Component getCustomEditor() {
1966: if (isI18nAutoMode())
1967: return i18nService.getCreateLocaleComponent(this,
1968: getSourceFile(), getI18nBundleName());
1969: else if (isResourceAutoMode())
1970: return resourceService.getCreateLocaleComponent(this,
1971: getSourceFile());
1972:
1973: return null;
1974: }
1975: }
1976: }
|