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.j2ee.wizard;
043:
044: import java.io.*;
045: import java.util.*;
046: import org.netbeans.api.queries.FileEncodingQuery;
047: import org.netbeans.modules.form.project.ClassPathUtils;
048: import org.openide.filesystems.FileLock;
049: import org.openide.filesystems.FileObject;
050:
051: /**
052: * Generator of master/detail form.
053: *
054: * @author Jan Stola
055: */
056: public class MasterDetailGenerator {
057: /** Name of the template for the label component. */
058: private static final String LABEL_TEMPLATE = "LABEL_TEMPLATE"; // NOI18N
059: /** Name of the template for the field component. */
060: private static final String FIELD_TEMPLATE = "FIELD_TEMPLATE"; // NOI18N
061: /** Name of the template for the master subbindigs. */
062: private static final String MASTER_SUBBINDING_TEMPLATE = "MASTER_SUBBINDING_TEMPLATE"; // NOI18N
063: /** Name of the template for the detail subbindigs. */
064: private static final String DETAIL_SUBBINDING_TEMPLATE = "DETAIL_SUBBINDING_TEMPLATE"; // NOI18N
065: /** Name of the template for the vertical layout. */
066: private static final String V_LAYOUT_TEMPLATE = "V_LAYOUT_TEMPLATE"; // NOI18N
067: /** Name of the template for the horizontal layout of labels. */
068: private static final String LABEL_H_LAYOUT_TEMPLATE = "LABEL_H_LAYOUT_TEMPLATE"; // NOI18N
069: /** Name of the template for the horizontal layout of fields. */
070: private static final String FIELD_H_LAYOUT_TEMPLATE = "FIELD_H_LAYOUT_TEMPLATE"; // NOI18N
071: /** Name of the sections that are valid only when the detail table is specified. */
072: private static final String DETAIL_ONLY = "DETAIL_ONLY"; // NOI18N
073: /** Name of the sections that are valid only when the detail table is not specified. */
074: private static final String MASTER_ONLY = "MASTER_ONLY"; // NOI18N
075: /** Name of the sections that are valid only when the template is created on JDK1.6. */
076: private static final String JDK6ONLY = "JDK6ONLY"; // NOI18N
077: /** Form file. */
078: private FileObject formFile;
079: /** Java file. */
080: private FileObject javaFile;
081: /** Class name of the master entity. */
082: private String masterClass;
083: /** Class name of the detail entity. */
084: private String detailClass;
085: /** Name of the master entity. */
086: private String masterEntity;
087: /** Name of the detail entity. */
088: private String detailEntity;
089: /** Name of the join property. */
090: private String joinProperty;
091: /** Name of the join collection property. */
092: private String joinCollectionProperty;
093: /** Name of the persistence unit. */
094: private String unit;
095: /** Columns of the master table. */
096: private List<String> masterColumns;
097: /** Columns of the detail table. */
098: private List<String> detailColumns;
099: /** Types of columns in master table. */
100: private List<String> masterColumnTypes;
101: /** Types of columns in detail table. */
102: private List<String> detailColumnTypes;
103:
104: /**
105: * Creates new <code>MasterDetailGenerator</code>.
106: *
107: * @param formFile form file.
108: * @param javaFile java file.
109: * @param masterClass class name of the master entity.
110: * @param detailClass class name of the detail entity.
111: * @param masterEntity name of the master entity.
112: * @param detailEntity name of the detail entity.
113: * @param joinProperty name of the join/fk property.
114: * @param joinCollectionProperty name of the join/fk collection property.
115: * @param unit name of the persistence unit.
116: */
117: MasterDetailGenerator(FileObject formFile, FileObject javaFile,
118: String masterClass, String detailClass,
119: String masterEntity, String detailEntity,
120: String joinProperty, String joinCollectionProperty,
121: String unit) {
122: this .formFile = formFile;
123: this .javaFile = javaFile;
124: this .masterClass = masterClass;
125: this .detailClass = detailClass;
126: this .masterEntity = masterEntity;
127: this .detailEntity = detailEntity;
128: this .joinProperty = joinProperty;
129: this .joinCollectionProperty = joinCollectionProperty;
130: this .unit = unit;
131: }
132:
133: /**
134: * Sets columns of the master table.
135: *
136: * @param masterColumns columns of the master table.
137: */
138: void setMasterColumns(List<String> masterColumns) {
139: this .masterColumns = masterColumns;
140: }
141:
142: void setMasterColumnTypes(List<String> masterColumnTypes) {
143: this .masterColumnTypes = masterColumnTypes;
144: }
145:
146: /**
147: * Sets columns of the detail table.
148: *
149: * @param detailColumns columns of the detail table.
150: */
151: void setDetailColumns(List<String> detailColumns) {
152: this .detailColumns = detailColumns;
153: }
154:
155: void setDetailColumnTypes(List<String> detailColumnTypes) {
156: this .detailColumnTypes = detailColumnTypes;
157: }
158:
159: /**
160: * Generates the master/detail form.
161: *
162: * @throws IOException if the generation fails.
163: */
164: void generate() throws IOException {
165: String formEncoding = "UTF-8"; // NOI18N
166: String javaEncoding = FileEncodingQuery.getDefaultEncoding()
167: .name();
168: String form = read(formFile, formEncoding);
169: String java = read(javaFile, javaEncoding);
170: Map<String, String> replacements = replacements();
171: for (Map.Entry<String, String> entry : replacements.entrySet()) {
172: form = form.replace(entry.getKey(), entry.getValue());
173: java = java.replace(entry.getKey(), entry.getValue());
174: }
175: form = generateMasterColumns(form);
176:
177: if (detailEntity == null) {
178: form = generateLabels(form);
179: form = generateFields(form);
180: form = generateVLayout(form);
181: form = generateLabelsHLayout(form);
182: form = generateFieldsHLayout(form);
183: form = deleteSections(form, DETAIL_ONLY, false, false);
184: form = deleteSections(form, MASTER_ONLY, true, false);
185: java = deleteSections(java, DETAIL_ONLY, false, true);
186: } else {
187: form = generateDetailColumns(form);
188: java = deleteSections(java, DETAIL_ONLY, true, true);
189: form = deleteSections(form, MASTER_ONLY, false, false);
190: form = deleteSections(form, DETAIL_ONLY, true, false);
191: }
192: java = deleteSections(java, JDK6ONLY, ClassPathUtils
193: .isJava6ProjectPlatform(javaFile), true);
194:
195: write(formFile, form, formEncoding);
196: write(javaFile, java, javaEncoding);
197: }
198:
199: /**
200: * Generates the content specified by <code>MASTER_SUBBINDING_TEMPLATE</code>.
201: *
202: * @param result the data being regenerated.
203: * @return result of the generation.
204: */
205: private String generateMasterColumns(String result) {
206: String template = findTemplate(result,
207: MASTER_SUBBINDING_TEMPLATE);
208: int index = result.indexOf(template);
209: result = result.substring(0, index)
210: + result.substring(index + template.length());
211: template = uncomment(template, false);
212:
213: StringBuilder sb = new StringBuilder();
214: int i = 0;
215: Iterator<String> iter = masterColumnTypes.iterator();
216: for (String column : masterColumns) {
217: String binding = template.replace("_index_", "" + i++); // NOI18N
218: binding = binding.replace("_fieldName_", column); // NOI18N
219: String type = iter.next();
220: if (type == null) { // fallback - shouldn't happen - means corrupted entity
221: type = "Object.class"; // NOI18N
222: }
223: binding = binding.replace("_fieldType_", type); // NOI18N
224: sb.append(binding);
225: }
226: StringBuilder rsb = new StringBuilder(result);
227: rsb.insert(index, sb.toString());
228: return rsb.toString();
229: }
230:
231: /**
232: * Generates the content specified by <code>DETAIL_SUBBINDING_TEMPLATE</code>.
233: *
234: * @param result the data being regenerated.
235: * @return result of the generation.
236: */
237: private String generateDetailColumns(String result) {
238: String template = findTemplate(result,
239: DETAIL_SUBBINDING_TEMPLATE);
240: int index = result.indexOf(template);
241: result = result.substring(0, index)
242: + result.substring(index + template.length());
243: template = uncomment(template, false);
244:
245: StringBuilder sb = new StringBuilder();
246: int i = 0;
247: Iterator<String> iter = detailColumnTypes.iterator();
248: for (String column : detailColumns) {
249: String binding = template.replace("_index_", "" + i++); // NOI18N
250: binding = binding.replace("_fieldName_", column); // NOI18N
251: binding = binding.replace("_fieldType_", iter.next()); // NOI18N
252: sb.append(binding);
253: }
254: StringBuilder rsb = new StringBuilder(result);
255: rsb.insert(index, sb.toString());
256: return rsb.toString();
257: }
258:
259: /**
260: * Generates the content specified by <code>LABEL_TEMPLATE</code>.
261: *
262: * @param result the data being regenerated.
263: * @return result of the generation.
264: */
265: private String generateLabels(String result) {
266: String template = findTemplate(result, LABEL_TEMPLATE);
267: int index = result.indexOf(template);
268: result = result.substring(0, index)
269: + result.substring(index + template.length());
270: template = uncomment(template, false);
271:
272: StringBuilder sb = new StringBuilder();
273: for (String column : detailColumns) {
274: String binding = template.replace("_labelName_",
275: columnToLabelName(column)); // NOI18N
276: binding = binding
277: .replace("_labelText_", capitalize(column)); // NOI18N
278: sb.append(binding);
279: }
280: StringBuilder rsb = new StringBuilder(result);
281: rsb.insert(index, sb.toString());
282: return rsb.toString();
283: }
284:
285: /**
286: * Generates the content specified by <code>FIELD_TEMPLATE</code>.
287: *
288: * @param result the data being regenerated.
289: * @return result of the generation.
290: */
291: private String generateFields(String result) {
292: String template = findTemplate(result, FIELD_TEMPLATE);
293: int index = result.indexOf(template);
294: result = result.substring(0, index)
295: + result.substring(index + template.length());
296: template = uncomment(template, false);
297:
298: StringBuilder sb = new StringBuilder();
299: for (String column : detailColumns) {
300: String binding = template.replace("_textFieldName_",
301: columnToFieldName(column)); // NOI18N
302: binding = binding.replace("_fieldName_", column); // NOI18N
303: sb.append(binding);
304: }
305: StringBuilder rsb = new StringBuilder(result);
306: rsb.insert(index, sb.toString());
307: return rsb.toString();
308: }
309:
310: /**
311: * Generates the content specified by <code>V_LAYOUT_TEMPLATE</code>.
312: *
313: * @param result the data being regenerated.
314: * @return result of the generation.
315: */
316: private String generateVLayout(String result) {
317: String template = findTemplate(result, V_LAYOUT_TEMPLATE);
318: int index = result.indexOf(template);
319: result = result.substring(0, index)
320: + result.substring(index + template.length());
321: template = uncomment(template, false);
322:
323: StringBuilder sb = new StringBuilder();
324: for (String column : detailColumns) {
325: String binding = template.replace("_labelName_",
326: columnToLabelName(column)); // NOI18N
327: binding = binding.replace("_textFieldName_",
328: columnToFieldName(column)); // NOI18N
329: sb.append(binding);
330: }
331: StringBuilder rsb = new StringBuilder(result);
332: rsb.insert(index, sb.toString());
333: return rsb.toString();
334: }
335:
336: /**
337: * Generates the content specified by <code>LABEL_H_LAYOUT_TEMPLATE</code>.
338: *
339: * @param result the data being regenerated.
340: * @return result of the generation.
341: */
342: private String generateLabelsHLayout(String result) {
343: String template = findTemplate(result, LABEL_H_LAYOUT_TEMPLATE);
344: int index = result.indexOf(template);
345: result = result.substring(0, index)
346: + result.substring(index + template.length());
347: template = uncomment(template, false);
348:
349: StringBuilder sb = new StringBuilder();
350: for (String column : detailColumns) {
351: String binding = template.replace("_labelName_",
352: columnToLabelName(column)); // NOI18N
353: sb.append(binding);
354: }
355: StringBuilder rsb = new StringBuilder(result);
356: rsb.insert(index, sb.toString());
357: return rsb.toString();
358: }
359:
360: /**
361: * Generates the content specified by <code>FIELD_H_LAYOUT_TEMPLATE</code>.
362: *
363: * @param result the data being regenerated.
364: * @return result of the generation.
365: */
366: private String generateFieldsHLayout(String result) {
367: String template = findTemplate(result, FIELD_H_LAYOUT_TEMPLATE);
368: int index = result.indexOf(template);
369: result = result.substring(0, index)
370: + result.substring(index + template.length());
371: template = uncomment(template, false);
372:
373: StringBuilder sb = new StringBuilder();
374: for (String column : detailColumns) {
375: String binding = template.replace("_textFieldName_",
376: columnToFieldName(column)); // NOI18N
377: sb.append(binding);
378: }
379: StringBuilder rsb = new StringBuilder(result);
380: rsb.insert(index, sb.toString());
381: return rsb.toString();
382: }
383:
384: /**
385: * Removes sections with the specified name from the given text.
386: *
387: * @param result text to remove the sections from.
388: * @param sectionName name of the sections to remove.
389: * @param commentsOnly determines whether to remove the whole secion
390: * or just the comment tags.
391: * @param java determines whether the section is marked by Java or HTML/XML comment.
392: * @return text with the specified sections removed.
393: */
394: private static String deleteSections(String result,
395: String sectionName, boolean commentsOnly, boolean java) {
396: String section = comment(sectionName, java);
397: int begin;
398: if (commentsOnly) {
399: while ((begin = result.indexOf(section)) != -1) {
400: result = result.substring(0, begin)
401: + result.substring(begin + section.length());
402: }
403: } else {
404: while ((begin = result.indexOf(section)) != -1) {
405: int end = result.indexOf(section, begin + 1);
406: assert (end != -1);
407: result = result.substring(0, begin)
408: + result.substring(end + section.length());
409: }
410: }
411: return result;
412: }
413:
414: /**
415: * Comments (e.g. adds the HTML/XML or Java comment) around the given text.
416: *
417: * @param comment comment that should be wrapped.
418: * @param java determines whether to use Java or HTML/XML comments.
419: * @return wrapped comment.
420: */
421: private static String comment(String comment, boolean java) {
422: String open = java ? "/*" : "<!--"; // NOI18N
423: String close = java ? "*/" : "-->"; // NOI18N
424: return open + " " + comment + " " + close; // NOI18N
425: }
426:
427: /**
428: * Uncomments (e.g. removes the HTML comment tags) from the given text.
429: *
430: * @param comment comment that should be unwrapped.
431: * @param java determines whether to use Java or HTML/XML comments.
432: * @return unwrapped comment.
433: */
434: private static String uncomment(String comment, boolean java) {
435: String open = java ? "/*" : "<!--"; // NOI18N
436: String close = java ? "*/" : "-->"; // NOI18N
437: int begin = comment.indexOf(close); // NOI18N
438: int end = comment.lastIndexOf(open); // NOI18N
439: return comment.substring(begin + open.length(), end);
440: }
441:
442: /**
443: * Returns name of the label that corresponds to the given column.
444: *
445: * @param column name of the column.
446: * @return name of the label that corresponds to the given column.
447: */
448: private static String columnToLabelName(String column) {
449: return column + "Label"; // NOI18N
450: }
451:
452: /**
453: * Returns name of the field that corresponds to the given column.
454: *
455: * @param column name of the column.
456: * @return name of the field that corresponds to the given column.
457: */
458: private static String columnToFieldName(String column) {
459: return column + "Field"; // NOI18N
460: }
461:
462: /**
463: * Reads the content of the file.
464: *
465: * @param file file whose content should be read.
466: * @return the content of the file.
467: * @throws IOException when the reading fails.
468: */
469: private static String read(FileObject file, String encoding)
470: throws IOException {
471: InputStream is = file.getInputStream();
472: BufferedReader br = new BufferedReader(new InputStreamReader(
473: is, encoding));
474: StringBuilder sb = new StringBuilder();
475: String s;
476: while ((s = br.readLine()) != null) {
477: sb.append(s).append('\n');
478: }
479: br.close();
480: return sb.toString();
481: }
482:
483: /**
484: * Writes the content of the file.
485: *
486: * @param file file whose content should be written.
487: * @param content new content of the file.
488: * @throws IOException when the writing fails.
489: */
490: private static void write(FileObject file, String content,
491: String encoding) throws IOException {
492: FileLock lock = file.lock();
493: try {
494: OutputStream os = file.getOutputStream(lock);
495: os.write(content.getBytes(encoding));
496: os.close();
497: } finally {
498: lock.releaseLock();
499: }
500: }
501:
502: /**
503: * Returns map of general replacements.
504: *
505: * @return map of general replacements.
506: */
507: private Map<String, String> replacements() {
508: Map<String, String> map = new HashMap<String, String>();
509: map.put("_masterClass_", masterClass); // NOI18N
510: map.put("_masterEntity_", masterEntity); // NOI18N
511: char masterInitial = Character.toLowerCase(masterEntity
512: .charAt(0));
513: map.put("_unitName_", unit); // NOI18N
514: if (detailClass != null) {
515: map.put("_detailClass_", detailClass); // NOI18N
516: map.put("_detailEntity_", detailEntity); // NOI18N
517: char detailInitial = Character.toLowerCase(detailEntity
518: .charAt(0));
519: map.put("_detailEntityInitial_", Character
520: .toString(detailInitial)); // NOI18N
521: if (detailInitial == masterInitial) {
522: masterInitial = Character.toUpperCase(masterInitial);
523: }
524: if (joinCollectionProperty != null) {
525: map.put("_joinCollection_", joinCollectionProperty); // NOI18N
526: map.put("_joinCollectionCapital_", Character
527: .toUpperCase(joinCollectionProperty.charAt(0))
528: + joinCollectionProperty.substring(1)); // NOI18N
529: }
530: if (joinProperty != null) {
531: map.put("_joinCapital_", Character
532: .toUpperCase(joinProperty.charAt(0))
533: + joinProperty.substring(1)); // NOI18N
534: }
535: }
536: map.put("_masterEntityInitial_", Character
537: .toString(masterInitial)); // NOI18N
538: return map;
539: }
540:
541: /**
542: * Finds the content of the template.
543: *
544: * @param where where the template should be found.
545: * @param templateName name of the template.
546: * @return the content of the template.
547: */
548: private static String findTemplate(String where, String templateName) {
549: String template = comment(templateName, false);
550: int index1 = where.indexOf(template);
551: int index2 = where.lastIndexOf(template);
552: return where.substring(index1, index2 + template.length());
553: }
554:
555: /**
556: * Trasformation aka userName -> User Name.
557: *
558: * @param title title to transform.
559: * @return transformed title.
560: */
561: private static String capitalize(String title) {
562: StringBuilder builder = new StringBuilder(title);
563: boolean lastWasUpper = false;
564: for (int i = 0; i < builder.length(); i++) {
565: char aChar = builder.charAt(i);
566: if (i == 0) {
567: builder.setCharAt(i, Character.toUpperCase(aChar));
568: lastWasUpper = true;
569: } else if (Character.isUpperCase(aChar)) {
570: if (!lastWasUpper) {
571: builder.insert(i, ' ');
572: }
573: lastWasUpper = true;
574: i++;
575: } else {
576: lastWasUpper = false;
577: }
578: }
579: return builder.toString();
580: }
581:
582: }
|