001: package tide.editor;
002:
003: import japa.parser.ast.body.ModifierSet;
004: import japa.parser.ast.body.TypeDeclaration;
005: import japa.parser.ast.CompilationUnit;
006: import java.io.StringReader;
007: import japa.parser.JavaParser;
008: import tide.utils.SyntaxUtils;
009: import snow.utils.StringUtils;
010: import javax.swing.border.EmptyBorder;
011: import java.io.File;
012: import tide.sources.FileItem;
013: import tide.sources.TypeLocator;
014: import java.awt.*;
015: import java.awt.event.*;
016: import javax.swing.*;
017: import snow.utils.gui.*;
018:
019: /** An interactive new source creation with preview, showing warnings for bad names and
020: * allowing for main method and singleton, class, interfaces, annotations, enums definitions
021: * with real-time code view.
022: */
023: public class NewSourceDialog extends JDialog {
024: // final private String packageName;
025: private JComboBox typeCB = new JComboBox(new String[] { "class",
026: "interface", "enumeration", "annotation", "package-info" });
027: private JTextField nameTF = new JTextField(20);
028: private JTextField packageTF = new JTextField(20);
029:
030: private JLabel warningLabel = new JLabel("");
031:
032: // for classes
033: private JCheckBox mainMethod = new JCheckBox("main method", true);
034: private JCheckBox singleton = new JCheckBox("singleton", false);
035:
036: private JCheckBox comments = new JCheckBox("comments", true);
037: private JButton sourcePaster = new JButton("Paster");
038:
039: private JTextArea preview = new JTextArea(17, 40);
040: private CloseControlPanel ccp;
041:
042: private String pastedSource = null;
043: private final JLabel warningLabel2 = new JLabel(
044: "<html>WARNING: the unnamed scope (root package) should NOT be used for sources.<br> Two arguments: sources here are not accessible from another package and JavaDoc ignore them.<br> Create only a source here if you have a good reason.");
045:
046: public NewSourceDialog(JFrame owner, final String packageName,
047: final String contentToPaste) {
048: super (owner, "Add New Source", true); // modal
049: //"New source in "+(packageName.length()>0 ? "package "+packageName: "the unnamed scope"), true);
050: // this.packageName = packageName;
051:
052: JPanel inputPanel = new JPanel();
053: inputPanel.setBorder(new EmptyBorder(5, 3, 2, 1));
054: inputPanel
055: .setLayout(new BoxLayout(inputPanel, BoxLayout.Y_AXIS));
056: inputPanel.setAlignmentX(0f);
057: add(inputPanel, BorderLayout.NORTH);
058:
059: JPanel input1 = new JPanel();
060: input1.setAlignmentX(0f);
061: inputPanel.add(input1);
062: GridLayout3 gl1 = new GridLayout3(2, input1);
063: gl1.add(" package");
064: gl1.add(packageTF);
065: packageTF.setText(packageName);
066: //si packageTF.setEditable(false);
067:
068: gl1.add(typeCB);
069: gl1.add(nameTF);
070:
071: comments.setSelected(MainEditorFrame.instance.globalProperties
072: .getBoolean("NewSourceDialog.GenerateComments", true));
073:
074: typeCB.setPreferredSize(new Dimension((int) typeCB
075: .getPreferredSize().getWidth(), (int) nameTF
076: .getPreferredSize().getHeight()));
077:
078: final JPanel classOptPanel = new JPanel();
079: classOptPanel.setAlignmentX(0f);
080: classOptPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
081: inputPanel.add(classOptPanel);
082:
083: inputPanel.add(warningLabel);
084: warningLabel.setForeground(Color.red);
085:
086: warningLabel2.setForeground(Color.red);
087: inputPanel.add(warningLabel2);
088:
089: if (packageName.length() == 0) {
090: warningLabel2.setVisible(true);
091: } else {
092: warningLabel2.setVisible(false);
093: }
094:
095: classOptPanel.add(comments);
096: classOptPanel.add(mainMethod);
097: classOptPanel.add(singleton);
098:
099: classOptPanel.add(sourcePaster);
100: GUIUtils.makeNiceButton(sourcePaster);
101:
102: sourcePaster.addActionListener(new ActionListener() {
103: public void actionPerformed(ActionEvent ae) {
104: sourcePaster();
105: }
106: });
107:
108: add(new JScrollPane(preview), BorderLayout.CENTER);
109: preview.setEditable(false);
110:
111: ccp = new CloseControlPanel(this , true, true, "Create");
112: ccp.getOkButton().setIcon(Icons.sharedPlus);
113: add(ccp, BorderLayout.SOUTH);
114:
115: typeCB.addActionListener(new ActionListener() {
116: public void actionPerformed(ActionEvent ae) {
117: if (typeCB.getSelectedIndex() == 4) {
118: nameTF.setText("" + typeCB.getSelectedItem());
119: }
120: nameTF.setEditable(typeCB.getSelectedIndex() != 4);
121:
122: classOptPanel
123: .setVisible(typeCB.getSelectedIndex() == 0);
124: checkNameValidity();
125: updatePreview();
126: }
127: });
128:
129: nameTF.addKeyListener(new KeyAdapter() {
130: @Override
131: public void keyReleased(KeyEvent ke) {
132: if (getJavaSimpleName().equals("package-info")) {
133: typeCB.setSelectedIndex(4);
134: typeCB.requestFocus();
135: return;
136: }
137: checkNameValidity();
138: updatePreview();
139:
140: if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
141: ccp.acceptAndClose();
142: }
143: }
144: });
145:
146: ActionListener upli = new ActionListener() {
147: public void actionPerformed(ActionEvent ae) {
148: updatePreview();
149: }
150: };
151: mainMethod.addActionListener(upli);
152: singleton.addActionListener(upli);
153: comments.addActionListener(upli);
154:
155: this .pack();
156: // center it on the screen :
157: this .setLocationRelativeTo(null);
158:
159: nameTF.requestFocusInWindow();
160:
161: if (contentToPaste != null) {
162: pasteContent(contentToPaste);
163: }
164:
165: this .setVisible(true); // MODAL
166:
167: MainEditorFrame.instance.globalProperties.setBoolean(
168: "NewSourceDialog.GenerateComments", comments
169: .isSelected());
170: }
171:
172: public boolean wasAccepted() {
173: return ccp.getWasAccepted() && !ccp.getWasCancelled();
174: }
175:
176: private void updatePreview() {
177: preview.setText(generateSourceContent());
178: preview.setCaretPosition(0);
179: }
180:
181: public String getJavaSimpleName() {
182: return this .nameTF.getText().trim();
183: }
184:
185: public String getPackageName() {
186: return this .packageTF.getText().trim();
187: }
188:
189: public String getJavaFullName() {
190: if (getPackageName().length() > 0)
191: return getPackageName() + "." + getJavaSimpleName();
192: else
193: return getJavaSimpleName();
194: }
195:
196: public String generateSourceContent() {
197: if (pastedSource != null && pastedSource.trim().length() > 0)
198: return pastedSource;
199:
200: StringBuilder sb = new StringBuilder();
201: if (getPackageName().length() > 0) {
202: sb.append("package " + getPackageName() + ";\n");
203: }
204:
205: String name = getJavaSimpleName();
206: if (name.length() == 0) {
207: name = "???";
208: }
209:
210: switch (typeCB.getSelectedIndex()) {
211: case 0:
212: if (comments.isSelected())
213: sb.append("\n/** class " + name + ".\n*/");
214: sb.append("\npublic final class " + name);
215: sb.append("\n{");
216:
217: if (comments.isSelected())
218: sb.append("\n /** Constructor. */");
219:
220: if (this .singleton.isSelected()) {
221: sb.append("\n private " + name + "()\n {\n }");
222: sb.append("\n private static " + name
223: + " instance = null;");
224: sb.append("\n public static " + name
225: + " getInstance()\n {");
226: sb
227: .append("\n if(instance==null) { instance = new "
228: + name + "(); }");
229: sb.append("\n return instance;\n }");
230: } else {
231: sb.append("\n public " + name + "()\n {\n }");
232: }
233:
234: if (this .mainMethod.isSelected()) {
235: sb
236: .append("\n\n public static void main(String[] args)\n {\n");
237: if (this .singleton.isSelected()) {
238: sb.append(" " + name + ".getInstance();\n");
239: } else {
240: sb.append(" new " + name + "();\n");
241: }
242: sb.append(" }\n");
243: }
244:
245: sb.append("\n}");
246: //if(comments.isSelected()) sb.append(" // "+name);
247: break;
248: case 1:
249: if (comments.isSelected())
250: sb.append("\n/** interface " + name + ".\n*/");
251: sb.append("\npublic interface " + name); // interfaces can't be final
252: sb.append("\n{\n}");
253: if (comments.isSelected())
254: sb.append(" // " + name);
255: break;
256: case 2:
257: sb.append("\npublic enum " + name);
258: sb.append("\n{\n ONE, TWO, THREE");
259: sb.append("\n}");
260: if (comments.isSelected())
261: sb.append(" // " + name);
262: break;
263: case 3:
264: sb.append("\nimport java.lang.annotation.*;");
265: sb.append("\n\n@Retention(RetentionPolicy.RUNTIME)");
266: sb.append("\n@Target(ElementType.METHOD)");
267: sb.append("\npublic @interface " + name);
268: sb.append("\n{");
269: sb.append("\n String value();");
270: sb.append("\n}");
271: if (comments.isSelected())
272: sb.append(" // " + name);
273: break;
274: case 4:
275: sb.setLength(0); // IGNORE CODE ABOVE
276: sb
277: .append("/** "
278: + (comments.isSelected() ? "package description for javaDoc."
279: : "") + "\r\n *\r\n */");
280: if (getPackageName().length() > 0) {
281: sb.append("\r\npackage " + getPackageName() + ";\r\n");
282: }
283: break;
284: }
285:
286: return sb.toString();
287: }
288:
289: public boolean checkNameValidity() {
290:
291: if (getPackageName().length() == 0) {
292: warningLabel2.setVisible(true);
293: } else {
294: warningLabel2.setVisible(false);
295: }
296:
297: warningLabel.setText("");
298:
299: nameTF
300: .setForeground(UIManager
301: .getColor("Textfield.foreground"));
302: if (this .typeCB.getSelectedIndex() == 4)
303: return true; // ok
304: String tf = nameTF.getText();
305: if (tf.length() == 0) {
306: nameTF.setForeground(Color.red);
307: return false;
308: }
309:
310: //no, special case
311: // if(tf.equals("package-info")) return true;
312:
313: if (!Character.isJavaIdentifierStart(tf.charAt(0))) {
314: nameTF.setForeground(Color.red);
315: warningLabel.setText("Error: Invalid Java first letter "
316: + tf.charAt(0));
317: return false;
318: }
319:
320: if (Character.isLowerCase(tf.charAt(0))) {
321: warningLabel
322: .setText("Warning: First char of name should not be lowercase");
323: }
324:
325: for (int i = 1; i < tf.length(); i++) {
326: if (!Character.isJavaIdentifierPart(tf.charAt(i))) {
327: nameTF.setForeground(Color.red);
328: warningLabel.setText("Error: Invalid Java letter "
329: + tf.charAt(i));
330: return false;
331: }
332: }
333:
334: FileItem fi = TypeLocator.locateQuick(getJavaFullName());
335: if (fi != null) {
336: nameTF.setForeground(Color.red);
337: warningLabel
338: .setText("Error: a type with the same name already exists");
339: return false;
340:
341: }
342:
343: final File f = new File(
344: MainEditorFrame.instance.sourcesTreePanel
345: .getTreeModel().getSourcesRoot(),
346: getJavaFullName().replace('.', '/') + ".java");
347:
348: if (f.exists()) {
349: nameTF.setForeground(Color.red);
350: warningLabel
351: .setText(" Error: a type with the same name (but other case) already exists");
352: return false;
353: }
354:
355: return true;
356: }
357:
358: private void sourcePaster() {
359: JTextArea ta = new JTextArea();
360: if (pastedSource != null)
361: ta.setText(pastedSource);
362:
363: ta.setDropMode(DropMode.USE_SELECTION);
364: JDialog pasteDialog = new JDialog(this ,
365: "Paste the source content here:", true);
366: pasteDialog.add(new JScrollPane(ta), BorderLayout.CENTER);
367: pasteDialog.setSize(350, 350);
368: pasteDialog.setLocationRelativeTo(this );
369: CloseControlPanel ccp2 = new CloseControlPanel(pasteDialog,
370: true, true, "Ok");
371: pasteDialog.add(ccp2, BorderLayout.SOUTH);
372: pasteDialog.setVisible(true); // MODAL
373:
374: if (ccp2.getWasCancelled())
375: return; // abort
376:
377: String src = ta.getText().trim();
378: if (src.length() == 0)
379: return; // abort
380:
381: pasteContent(src);
382: }
383:
384: private void pasteContent(String src) {
385: preview.setText(src);
386: preview.setCaretPosition(0);
387: pastedSource = src;
388:
389: // use the javaparser !, don't extract so easily...
390: try {
391: CompilationUnit cu = JavaParser
392: .parse(new StringReader(src));
393: if (cu.pakage != null) {
394: packageTF.setText("" + cu.pakage.name);
395: } else {
396: packageTF.setText("");
397: }
398:
399: TypeDeclaration type = null;
400: if (cu.types != null) {
401: for (final TypeDeclaration it : cu.types) {
402: if (ModifierSet.isPublic(it.modifiers)) {
403: type = it;
404: break;
405: }
406: }
407: }
408:
409: if (type == null) {
410: if (cu.types != null && !cu.types.isEmpty()) {
411: type = cu.types.get(0);
412: }
413: }
414:
415: /* if(type==null)
416: {
417: JOptionPane.showMessageDialog(null, "No type found", "Error", JOptionPane.ERROR_MESSAGE);
418: }*/
419:
420: if (type != null) {
421: nameTF.setText(type.name);
422: typeCB.setSelectedIndex(0); // ok...
423: //if(type instanceof ClassOrInterfaceDeclaration
424: }
425:
426: } catch (Exception e) {
427: e.printStackTrace();
428:
429: // MANUAL UGLY PARSE...
430: // package
431: String pn = StringUtils.extractFromFirstToNext_Excluded(
432: src, "package ", ";");
433: if (pn != null && pn.trim().length() >= 0) {
434: packageTF.setText(pn.trim());
435: } else {
436: // let the same package, => and add it to the source
437: src = "package " + packageTF.getText() + ";\r\n\r\n"
438: + src;
439: }
440:
441: // source name (class, interface, annotation, enum !)
442: String cn = StringUtils.extractFromFirstToNext_Excluded(
443: src, "class ", " "); //BAD !, maybe an interface, maybe class XX{, maybe in a comment...
444: if (cn != null && cn.trim().length() > 0) {
445: cn = cn.trim();
446: if (SyntaxUtils.isValidIdentifier(cn)) {
447: nameTF.setText(cn);
448: typeCB.setSelectedIndex(0);
449: }
450: }
451: }
452:
453: //no, let the user validate or cancel itself, but freeze the other options
454: //ccp.acceptAndClose();
455:
456: freeze();
457: checkNameValidity();
458: }
459:
460: void freeze() {
461: //this.nameTF.setEditable(false);
462: //this.packageTF.setEditable(false);
463: this .typeCB.setEnabled(false);
464: this .mainMethod.setEnabled(false);
465: this .singleton.setEnabled(false);
466: this .comments.setEnabled(false);
467: }
468:
469: /*
470: public static void main(String[] arguments)
471: {
472: System.out.println(""+ StringUtils.extractFromFirstToNext_Excluded("class ZZZ {", "class ", " "));
473: }*/
474:
475: }
|