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-2006 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.lang.reflect.Modifier;
045: import java.util.Arrays;
046: import java.util.Collection;
047: import java.util.HashMap;
048: import java.util.Map;
049: import org.netbeans.api.editor.guards.SimpleSection;
050: import org.openide.DialogDescriptor;
051: import org.openide.DialogDisplayer;
052: import org.openide.ErrorManager;
053: import org.openide.NotifyDescriptor;
054: import org.openide.filesystems.FileObject;
055: import org.openide.util.Exceptions;
056: import org.openide.util.HelpCtx;
057: import org.openide.util.NbBundle;
058:
059: import static org.netbeans.modules.form.CustomCodeData.*;
060:
061: /**
062: * This class controls the code customization - opens the
063: * customizer dialog, reads and stores data, etc.
064: *
065: * @author Tomas Pavek
066: */
067:
068: public class CodeCustomizer implements CustomCodeView.Listener {
069:
070: private FormModel formModel;
071:
072: // changed code data for components
073: private Map<RADComponent, CustomCodeData> changedDataMap = new HashMap<RADComponent, CustomCodeData>();
074:
075: private CustomCodeView codeView;
076:
077: private RADComponent customizedComponent; // actual selected component
078:
079: private CodeCustomizer(FormModel formModel) {
080: this .formModel = formModel;
081: codeView = new CustomCodeView(this );
082: setupComponentNames();
083: }
084:
085: public static void show(RADComponent metacomp) {
086: CodeCustomizer customizer = new CodeCustomizer(metacomp
087: .getFormModel());
088: customizer.selectComponent(metacomp);
089: customizer.show();
090: }
091:
092: private void show() {
093: JavaCodeGenerator codeGen = (JavaCodeGenerator) FormEditor
094: .getCodeGenerator(formModel);
095: codeGen.regenerateCode(); // to have fresh code for code completion
096:
097: DialogDescriptor dd = new DialogDescriptor(codeView, NbBundle
098: .getMessage(CodeCustomizer.class,
099: "TITLE_CodeCustomizer"), // NOI18N
100: true, DialogDescriptor.OK_CANCEL_OPTION, null, null);
101: dd.setHelpCtx(new HelpCtx("gui.codecustomizer")); // NOI18N
102: Object res = DialogDisplayer.getDefault().notify(dd);
103: if (DialogDescriptor.OK_OPTION.equals(res)) {
104: retreiveCurrentData();
105: storeChanges();
106: }
107: }
108:
109: private void setupComponentNames() {
110: Collection<RADComponent> allComps = formModel
111: .getAllComponents();
112: RADComponent rootComp = formModel.getTopRADComponent();
113: String[] compNames = new String[rootComp != null ? allComps
114: .size() - 1 : allComps.size()];
115: int i = 0;
116: for (RADComponent metacomp : allComps) {
117: if (metacomp != rootComp) { // don't add root comp until it is supported (#87492)
118: compNames[i++] = metacomp.getName();
119: }
120: }
121: Arrays.sort(compNames, 0, compNames.length - 1);
122: codeView.setComponentNames(compNames);
123: }
124:
125: private void selectComponent(RADComponent metacomp) {
126: customizedComponent = metacomp;
127: CustomCodeData codeData = changedDataMap.get(metacomp);
128: if (codeData == null) {
129: codeData = JavaCodeGenerator.getCodeData(metacomp);
130: codeData.check();
131: }
132: codeView.setCodeData(customizedComponent.getName(), codeData,
133: getSourceFile(), getSourcePositions());
134: }
135:
136: private FileObject getSourceFile() {
137: return FormEditor.getFormDataObject(formModel).getPrimaryFile();
138: }
139:
140: /**
141: * @return array of 2 ints - positions of the customized code within entire
142: * source to be used for code completion; the first is the position
143: * of the init code, the second is position for the field variable
144: * declaration code
145: */
146: private int[] getSourcePositions() {
147: SimpleSection sec = FormEditor.getFormDataObject(formModel)
148: .getFormEditorSupport().getInitComponentSection();
149: return new int[] {
150: sec.getText().indexOf('{') + 2
151: + sec.getStartPosition().getOffset(),
152: sec.getEndPosition().getOffset() + 1 };
153: }
154:
155: private void retreiveCurrentData() {
156: if (codeView.isChanged()) {
157: changedDataMap.put(customizedComponent, codeView
158: .retreiveCodeData());
159: }
160: }
161:
162: private void storeChanges() {
163: for (Map.Entry<RADComponent, CustomCodeData> e : changedDataMap
164: .entrySet()) {
165: storeComponent(e.getKey(), e.getValue(), true);
166: }
167: changedDataMap.clear();
168: }
169:
170: private void storeComponent(RADComponent metacomp,
171: CustomCodeData codeData, boolean definite) {
172: storeCodeCategory(metacomp, codeData,
173: CodeCategory.CREATE_AND_INIT, definite);
174: storeCodeCategory(metacomp, codeData, CodeCategory.DECLARATION,
175: definite);
176: storeDeclaration(metacomp, codeData.getDeclarationData(),
177: definite);
178: }
179:
180: private static void storeCodeCategory(RADComponent metacomp,
181: CustomCodeData codeData, CodeCategory category,
182: boolean definite) {
183: int eCount = codeData.getEditableBlockCount(category);
184: for (int i = 0; i < eCount; i++) {
185: EditableBlock eBlock = codeData.getEditableBlock(category,
186: i);
187: for (CodeEntry e : eBlock.getEntries()) {
188: storeCodeEntry(metacomp, e, definite);
189: }
190: }
191: int gCount = codeData.getGuardedBlockCount(category);
192: for (int i = 0; i < gCount; i++) {
193: GuardedBlock gBlock = codeData.getGuardedBlock(category, i);
194: if (gBlock.isCustomizable()) {
195: storeCodeEntry(metacomp, gBlock.getCustomEntry(),
196: definite);
197: }
198: }
199: }
200:
201: private static void storeCodeEntry(RADComponent metacomp,
202: CodeEntry entry, boolean definite) {
203: FormProperty prop = entry.getTargetProperty();
204: String code = entry.getCode();
205:
206: boolean firing;
207: if (!definite) {
208: firing = prop.isChangeFiring();
209: prop.setChangeFiring(false);
210: } else
211: firing = true;
212:
213: try {
214: if (entry.isPropertyPreInit()) {
215: prop.setPreCode(code);
216: } else if (entry.isPropertyPostInit()) {
217: prop.setPostCode(code);
218: } else if (prop instanceof RADProperty) { // custom code for bean property
219: if (code != null) { // custom code specified
220: Object codeValue = new FormProperty.ValueWithEditor(
221: new RADConnectionPropertyEditor.RADConnectionDesignValue(
222: code),
223: new RADConnectionPropertyEditor(prop
224: .getValueType()));
225: prop.setValue(codeValue);
226: } else if (JavaCodeGenerator
227: .isPropertyWithCustomCode(prop)) { // default code
228: prop.restoreDefaultValue(); // cancel custom code
229: if (!definite && prop.getPreCode() == null
230: && prop.getPostCode() == null)
231: prop.setPreCode("\n"); // set something to get the property generated again // NOI18N
232: }
233: // otherwise do nothing (property does not contain custom code)
234: } else { // synthetic code property
235: prop.setValue(code != null ? code : ""); // NOI18N
236: }
237: } catch (Exception ex) { // should not happen
238: ErrorManager.getDefault().notify(
239: ErrorManager.INFORMATIONAL, ex);
240: }
241:
242: if (!definite)
243: prop.setChangeFiring(firing);
244: }
245:
246: private static void storeDeclaration(RADComponent metacomp,
247: VariableDeclaration decl, boolean definite) {
248: FormProperty varProp = (FormProperty) metacomp
249: .getSyntheticProperty(JavaCodeGenerator.PROP_VARIABLE_LOCAL);
250: FormProperty modifProp = (FormProperty) metacomp
251: .getSyntheticProperty(JavaCodeGenerator.PROP_VARIABLE_MODIFIER);
252:
253: boolean firing;
254: if (!definite) {
255: firing = varProp.isChangeFiring();
256: varProp.setChangeFiring(false);
257: modifProp.setChangeFiring(false);
258: } else
259: firing = true;
260:
261: try {
262: varProp.setValue(decl.local);
263: int modif = decl.modifiers;
264: if (modif < 0)
265: modif = (((Integer) modifProp.getValue()).intValue() & ~Modifier.FINAL)
266: | (modif & Modifier.FINAL);
267: modifProp.setValue(modif);
268: } catch (Exception ex) { // should not happen
269: ErrorManager.getDefault().notify(
270: ErrorManager.INFORMATIONAL, ex);
271: }
272:
273: if (!definite) {
274: varProp.setChangeFiring(firing);
275: modifProp.setChangeFiring(firing);
276: }
277: }
278:
279: // -----
280: // CustomCodeView.Listener implementation
281:
282: public void componentExchanged(String compName) {
283: retreiveCurrentData();
284: selectComponent(formModel.findRADComponent(compName));
285: }
286:
287: public void renameInvoked() {
288: NotifyDescriptor.InputLine input = new NotifyDescriptor.InputLine(
289: NbBundle.getMessage(CodeCustomizer.class,
290: "CTL_RenameLabel"), // NOI18N
291: NbBundle.getMessage(CodeCustomizer.class,
292: "CTL_RenameTitle")); // NOI18N
293: input.setInputText(customizedComponent.getName());
294:
295: if (NotifyDescriptor.OK_OPTION.equals(DialogDisplayer
296: .getDefault().notify(input))) {
297: // code data must be saved to component before renaming
298: retreiveCurrentData();
299: CustomCodeData codeData = changedDataMap
300: .get(customizedComponent);
301: if (codeData != null) {
302: NotifyDescriptor.Confirmation confirm = new NotifyDescriptor.Confirmation(
303: NbBundle.getMessage(CodeCustomizer.class,
304: "CTL_ApplyChangesLabel"), // NOI18N
305: NbBundle.getMessage(CodeCustomizer.class,
306: "CTL_ApplyChangesTitle"), // NOI18N
307: NotifyDescriptor.OK_CANCEL_OPTION);
308: if (!NotifyDescriptor.OK_OPTION.equals(DialogDisplayer
309: .getDefault().notify(confirm)))
310: return;
311:
312: storeComponent(customizedComponent, codeData, true);
313: changedDataMap.remove(customizedComponent);
314: }
315:
316: try {
317: String newName = input.getInputText();
318: if (!newName.equals("")) // NOI18N
319: customizedComponent.rename(newName);
320: } catch (IllegalArgumentException e) {
321: Exceptions.printStackTrace(e);
322: return;
323: }
324:
325: setupComponentNames();
326: codeData = JavaCodeGenerator
327: .getCodeData(customizedComponent);
328: codeData.check();
329: codeView.setCodeData(customizedComponent.getName(),
330: codeData, getSourceFile(), getSourcePositions());
331: }
332: }
333:
334: public void declarationChanged() {
335: // remeber the current data - we'll return to it so there is no change in the model
336: CustomCodeData original = JavaCodeGenerator
337: .getCodeData(customizedComponent);
338: // write the customized configuration to the component
339: retreiveCurrentData();
340: CustomCodeData actual = changedDataMap.get(customizedComponent);
341: storeComponent(customizedComponent, actual, false);
342: // get the new code data for the changed configuartion
343: CustomCodeData renewed = JavaCodeGenerator
344: .getCodeData(customizedComponent);
345: renewed.check();
346: // restore the original data in the component
347: storeComponent(customizedComponent, original, false);
348: // set the new data to the view
349: codeView.setCodeData(customizedComponent.getName(), renewed,
350: getSourceFile(), getSourcePositions());
351: }
352: }
|