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.j2ee.wizard;
0043:
0044: import com.sun.source.tree.*;
0045: import java.beans.PropertyChangeEvent;
0046: import java.beans.PropertyChangeListener;
0047: import java.io.File;
0048: import java.io.IOException;
0049: import java.util.*;
0050: import java.util.concurrent.ExecutionException;
0051: import java.util.logging.Level;
0052: import java.util.logging.Logger;
0053: import javax.lang.model.element.Modifier;
0054: import javax.lang.model.element.TypeElement;
0055: import javax.lang.model.type.TypeKind;
0056: import javax.swing.JComponent;
0057: import javax.swing.event.ChangeListener;
0058: import org.netbeans.api.db.explorer.DatabaseConnection;
0059: import org.netbeans.api.db.explorer.JDBCDriver;
0060: import org.netbeans.api.db.explorer.JDBCDriverManager;
0061: import org.netbeans.api.java.classpath.ClassPath;
0062: import org.netbeans.api.java.source.CancellableTask;
0063: import org.netbeans.api.java.source.JavaSource;
0064: import org.netbeans.api.java.source.TreeMaker;
0065: import org.netbeans.api.java.source.WorkingCopy;
0066: import org.netbeans.api.project.FileOwnerQuery;
0067: import org.netbeans.api.project.Project;
0068: import org.netbeans.api.project.ui.OpenProjects;
0069: import org.netbeans.modules.form.j2ee.J2EEUtils;
0070: import org.netbeans.modules.j2ee.metadata.model.api.MetadataModel;
0071: import org.netbeans.modules.j2ee.metadata.model.api.MetadataModelAction;
0072: import org.netbeans.modules.j2ee.persistence.api.PersistenceScope;
0073: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Entity;
0074: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.EntityMappingsMetadata;
0075: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.ManyToOne;
0076: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.OneToMany;
0077: import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.PersistenceUnit;
0078: import org.netbeans.spi.java.project.support.ui.templates.JavaTemplates;
0079: import org.openide.WizardDescriptor;
0080: import org.openide.cookies.OpenCookie;
0081: import org.openide.filesystems.FileObject;
0082: import org.openide.filesystems.FileUtil;
0083: import org.openide.loaders.DataObject;
0084: import org.openide.loaders.DataObjectNotFoundException;
0085: import org.openide.util.NbBundle;
0086:
0087: /**
0088: * Master/detail wizard.
0089: *
0090: * @author Jan Stola
0091: */
0092: public class MasterDetailWizard implements
0093: WizardDescriptor.InstantiatingIterator {
0094: /** Key for the description of the wizard content. */
0095: private static final String WIZARD_PANEL_CONTENT_DATA = "WizardPanel_contentData"; // NOI18N
0096: /** Key for the description of the wizard panel's position. */
0097: private static final String WIZARD_PANEL_CONTENT_SELECTED_INDEX = "WizardPanel_contentSelectedIndex"; // NOI18N
0098: /** Index of the current panel. */
0099: private int panelIndex;
0100: /** Panels of this wizard. */
0101: private WizardDescriptor.Panel[] panels;
0102: /** Names of wizard steps. */
0103: private String[] steps;
0104: /** Wizard descriptor. */
0105: private WizardDescriptor wizard;
0106: /** Wizard iterator preceding our steps. */
0107: private transient WizardDescriptor.InstantiatingIterator delegateIterator;
0108: /** Number of steps of the delegate iterator (for configuring new file) */
0109: private int beforeStepsNo;
0110: private String[] joinInfo;
0111: private String joinColumn;
0112: private String referencedColumn;
0113:
0114: /**
0115: * Creates new <code>MasterDetailWizard</code>.
0116: *
0117: * @param createNewFile determines whether this wizard should create
0118: * a new master/detail form or whether it should add the master/detail
0119: * functionality into an existing file.
0120: */
0121: MasterDetailWizard(boolean createNewFile) {
0122: if (createNewFile)
0123: delegateIterator = JavaTemplates
0124: .createJavaTemplateIterator();
0125: }
0126:
0127: /**
0128: * Creates new master/detail wizard that creates new file.
0129: *
0130: * @return new instance of <code>MasterDetailWizard</code>.
0131: */
0132: public static MasterDetailWizard create() {
0133: return new MasterDetailWizard(true);
0134: }
0135:
0136: /**
0137: * Creates new master/detail wizard that processes existing file.
0138: *
0139: * @return new instance of <code>MasterDetailWizard</code>.
0140: */
0141: public static MasterDetailWizard createForExisting() {
0142: return new MasterDetailWizard(false);
0143: }
0144:
0145: /**
0146: * Initializes the wizard.
0147: *
0148: * @param wizard descriptor of this wizard.
0149: */
0150: public void initialize(WizardDescriptor wizard) {
0151: this .wizard = wizard;
0152: if (delegateIterator != null) {
0153: delegateIterator.initialize(wizard);
0154: }
0155: panelIndex = 0;
0156: panels = createPanels();
0157: }
0158:
0159: /**
0160: * Creates panels of this wizard.
0161: *
0162: * @return panels of this wizard.
0163: */
0164: private WizardDescriptor.Panel[] createPanels() {
0165: panels = new WizardDescriptor.Panel[] {
0166: new MasterPanel(delegateIterator == null),
0167: new DetailPanel() };
0168: return panels;
0169: }
0170:
0171: /**
0172: * Returns current panel of the wizard.
0173: *
0174: * @return current panel of the wizard.
0175: */
0176: public WizardDescriptor.Panel current() {
0177: String title = NbBundle.getMessage(MasterDetailWizard.class,
0178: "TITLE_MasterDetail"); // NOI18N
0179: wizard.putProperty("NewFileWizard_Title", title); // NOI18N
0180: if (steps == null) {
0181: initSteps();
0182: }
0183: WizardDescriptor.Panel panel;
0184: if (panelIndex < beforeStepsNo) {
0185: panel = delegateIterator.current();
0186: } else {
0187: panel = panels[panelIndex - beforeStepsNo];
0188: }
0189: JComponent comp = (JComponent) panel.getComponent();
0190: if ((panelIndex < beforeStepsNo)
0191: || (comp.getClientProperty(WIZARD_PANEL_CONTENT_DATA) == null)) {
0192: comp.putClientProperty(WIZARD_PANEL_CONTENT_DATA, steps);
0193: }
0194: if (comp.getClientProperty(WIZARD_PANEL_CONTENT_SELECTED_INDEX) == null) {
0195: comp.putClientProperty(WIZARD_PANEL_CONTENT_SELECTED_INDEX,
0196: panelIndex);
0197: }
0198: return panel;
0199: }
0200:
0201: /**
0202: * Initializes array of steps of this wizard.
0203: */
0204: private void initSteps() {
0205: String[] this Steps = new String[] {
0206: NbBundle.getMessage(MasterDetailWizard.class,
0207: "TITLE_MasterPanel"), // NOI18N
0208: NbBundle.getMessage(MasterDetailWizard.class,
0209: "TITLE_DetailPanel") // NOI18N
0210: };
0211:
0212: Object prop;
0213: if (delegateIterator != null) {
0214: JComponent comp = (JComponent) delegateIterator.current()
0215: .getComponent();
0216: prop = comp.getClientProperty("WizardPanel_contentData"); // NOI18N
0217: } else
0218: prop = null;
0219:
0220: String[] beforeSteps;
0221: int stepsStartPos;
0222:
0223: if (prop instanceof String[]) {
0224: beforeSteps = (String[]) prop;
0225: beforeStepsNo = beforeSteps.length;
0226: stepsStartPos = beforeSteps.length;
0227: if (stepsStartPos > 0
0228: && ("...".equals(beforeSteps[stepsStartPos - 1]))) { // NOI18N
0229: stepsStartPos--;
0230: }
0231: } else {
0232: beforeStepsNo = 0;
0233: beforeSteps = null;
0234: stepsStartPos = 0;
0235: }
0236:
0237: steps = new String[stepsStartPos + this Steps.length];
0238: if (beforeSteps != null)
0239: System.arraycopy(beforeSteps, 0, steps, 0, stepsStartPos);
0240: System.arraycopy(this Steps, 0, steps, stepsStartPos,
0241: this Steps.length);
0242: }
0243:
0244: /**
0245: * Returns name of the current panel.
0246: *
0247: * @return name of the current panel.
0248: */
0249: public String name() {
0250: return current().getComponent().getName();
0251: }
0252:
0253: /**
0254: * Determines whether there is a next panel.
0255: *
0256: * @return <code>true</code> if there is a next panel,
0257: * returns <code>false</code> otherwise.
0258: */
0259: public boolean hasNext() {
0260: if ((panelIndex + 1 < beforeStepsNo)
0261: && !delegateIterator.hasNext()) {
0262: // Delegate iterator doesn't manage all previous steps
0263: beforeStepsNo = panelIndex + 1;
0264: }
0265: return panelIndex + 1 < beforeStepsNo + panels.length;
0266: }
0267:
0268: /**
0269: * Determines whether there is a previous panel.
0270: *
0271: * @return <code>true</code> if there is a previous panel,
0272: * returns <code>false</code> otherwise.
0273: */
0274: public boolean hasPrevious() {
0275: return panelIndex > 0;
0276: }
0277:
0278: /**
0279: * Moves to the next panel.
0280: */
0281: public void nextPanel() {
0282: panelIndex++;
0283: if (panelIndex < beforeStepsNo)
0284: delegateIterator.nextPanel();
0285: }
0286:
0287: /**
0288: * Moves to the previous panel.
0289: */
0290: public void previousPanel() {
0291: panelIndex--;
0292: if (panelIndex < beforeStepsNo - 1)
0293: delegateIterator.previousPanel();
0294: }
0295:
0296: public Set instantiate() throws IOException {
0297: if (delegateIterator == null) {
0298: // postpone the instantiation until the project is opened
0299: final FileObject file = (FileObject) wizard
0300: .getProperty("mainForm"); // NOI18N
0301: final File projDir = (File) wizard.getProperty("projdir");
0302: OpenProjects.getDefault().addPropertyChangeListener(
0303: new PropertyChangeListener() {
0304: public void propertyChange(
0305: PropertyChangeEvent evt) {
0306: try {
0307: for (Project p : OpenProjects
0308: .getDefault().getOpenProjects()) {
0309: File dir = FileUtil.toFile(p
0310: .getProjectDirectory());
0311: if (projDir.equals(dir)) {
0312: try {
0313: instantiate0();
0314: // Open the generated frame
0315: DataObject dob = DataObject
0316: .find(file);
0317: OpenCookie cookie = dob
0318: .getCookie(OpenCookie.class);
0319: cookie.open();
0320: } catch (IOException ioex) {
0321: Logger
0322: .getLogger(
0323: getClass()
0324: .getName())
0325: .log(
0326: Level.INFO,
0327: ioex
0328: .getMessage(),
0329: ioex);
0330: }
0331: break;
0332: } // else something went wrong - don't attempt to instantiate
0333: }
0334: } finally {
0335: OpenProjects.getDefault()
0336: .removePropertyChangeListener(
0337: this );
0338: }
0339: }
0340: });
0341: return Collections.EMPTY_SET;
0342: } else {
0343: return instantiate0();
0344: }
0345: }
0346:
0347: /**
0348: * Returns set of instantiated objects.
0349: *
0350: * @return set of instantiated objects.
0351: * @throws IOException when the objects cannot be instantiated.
0352: */
0353: public Set instantiate0() throws IOException {
0354: Set resultSet = null;
0355: try {
0356: DatabaseConnection connection = (DatabaseConnection) wizard
0357: .getProperty("connection"); // NOI18N
0358: String masterTableName = (String) wizard
0359: .getProperty("master"); // NOI18N
0360: String detailFKTable = (String) wizard
0361: .getProperty("detailFKTable"); // NOI18N
0362: joinColumn = (String) wizard.getProperty("detailFKColumn"); // NOI18N
0363: referencedColumn = (String) wizard
0364: .getProperty("detailPKColumn"); // NOI18N
0365:
0366: FileObject javaFile;
0367: if (delegateIterator != null) {
0368: resultSet = delegateIterator.instantiate();
0369: javaFile = (FileObject) resultSet.iterator().next();
0370: } else {
0371: resultSet = new HashSet();
0372: javaFile = (FileObject) wizard.getProperty("mainForm"); // NOI18N
0373: resultSet.add(javaFile);
0374: }
0375: javaFile.setAttribute("justCreatedByNewWizard",
0376: Boolean.TRUE); // NOI18N
0377: DataObject dob = null;
0378: try {
0379: dob = DataObject.find(javaFile);
0380: } catch (DataObjectNotFoundException ex) {
0381: Logger.getLogger(getClass().getName()).log(Level.INFO,
0382: ex.getMessage(), ex); // should not happen
0383: }
0384: FileObject formFile = FileUtil.findBrother(dob
0385: .getPrimaryFile(), "form"); // NOI18N
0386:
0387: String[][] entity = instantiatePersitence(javaFile
0388: .getParent(), connection, masterTableName,
0389: detailFKTable);
0390:
0391: if (entity[0] == null) {
0392: System.err.println("WARNING: Cannot find entity: "
0393: + masterTableName); // NOI18N
0394: entity[0] = new String[] { "Object",
0395: Object.class.getName() }; // NOI18N
0396: }
0397: if ((detailFKTable != null) && (entity[1] == null)) {
0398: System.err.println("WARNING: Cannot find entity: "
0399: + detailFKTable); // NOI18N
0400: entity[1] = new String[] { "Object",
0401: Object.class.getName() }; // NOI18N
0402: }
0403:
0404: String masterClass = entity[0][1];
0405: String masterEntity = entity[0][0];
0406: String detailClass = null;
0407: String detailEntity = null;
0408:
0409: if (entity[1] != null) {
0410: detailClass = entity[1][1];
0411: detailEntity = entity[1][0];
0412: }
0413: MasterDetailGenerator generator = new MasterDetailGenerator(
0414: formFile, javaFile, masterClass, detailClass,
0415: masterEntity, detailEntity,
0416: (joinInfo == null) ? null : joinInfo[0],
0417: (joinInfo == null) ? null : joinInfo[1], unitName);
0418:
0419: List<String> masterColumnNames = (List<String>) wizard
0420: .getProperty("masterColumns"); // NOI18N
0421: List<String> masterColumns = J2EEUtils
0422: .propertiesForColumns(mappings, masterEntity,
0423: masterColumnNames);
0424: generator.setMasterColumns(masterColumns);
0425:
0426: List<String> masterColumnTypes = J2EEUtils
0427: .typesOfProperties(javaFile, masterClass,
0428: masterColumns);
0429: generator.setMasterColumnTypes(masterColumnTypes);
0430:
0431: List<String> detailColumnNames = (List<String>) wizard
0432: .getProperty("detailColumns"); // NOI18N
0433: List<String> detailColumns = J2EEUtils
0434: .propertiesForColumns(mappings,
0435: (detailEntity == null) ? masterEntity
0436: : detailEntity, detailColumnNames);
0437: generator.setDetailColumns(detailColumns);
0438:
0439: if (detailClass != null) {
0440: List<String> detailColumnTypes = J2EEUtils
0441: .typesOfProperties(javaFile, detailClass,
0442: detailColumns);
0443: generator.setDetailColumnTypes(detailColumnTypes);
0444: }
0445:
0446: generator.generate();
0447: } catch (Exception ex) {
0448: Logger.getLogger(getClass().getName()).log(Level.INFO,
0449: ex.getMessage(), ex);
0450: }
0451:
0452: return resultSet;
0453: }
0454:
0455: private MetadataModel<EntityMappingsMetadata> mappings;
0456: /** Name of persistence unit that contains entity classes for master and detail tables. */
0457: private String unitName;
0458:
0459: /**
0460: * Creates or updates persistence descriptor and entity classes for master and detail table.
0461: *
0462: * @param folder folder whether the entity classes should be generated.
0463: * @param connection connection to the database with master and detail table.
0464: * @param tableName name of the master table.
0465: * @param detailTable name of the detail table (can be <code>null</code>).
0466: * @return entities that correspond to master and detail tables.
0467: */
0468: private String[][] instantiatePersitence(FileObject folder,
0469: DatabaseConnection connection, String tableName,
0470: String detailTable) {
0471: Project project = FileOwnerQuery.getOwner(folder);
0472: try {
0473: // Make sure persistence.xml file exists
0474: FileObject persistenceXML = J2EEUtils.getPersistenceXML(
0475: project, true);
0476:
0477: // Initializes persistence unit and persistence descriptor
0478: PersistenceUnit unit = J2EEUtils.initPersistenceUnit(
0479: persistenceXML, connection);
0480: unitName = unit.getName();
0481:
0482: // Initializes project's classpath
0483: // PENDING solicit for DatabaseConnection.getJDBCDriver
0484: JDBCDriver[] driver = JDBCDriverManager.getDefault()
0485: .getDrivers(connection.getDriverClass());
0486: J2EEUtils.updateProjectForUnit(persistenceXML, unit,
0487: driver[0]);
0488:
0489: // Obtain description of entity mappings
0490: PersistenceScope scope = PersistenceScope
0491: .getPersistenceScope(folder);
0492: mappings = scope.getEntityMappingsModel(unit.getName());
0493:
0494: String[] tables;
0495: String[] relatedTables = null;
0496: if (J2EEUtils.TABLE_CLOSURE) {
0497: tables = new String[1];
0498: if (detailTable == null) {
0499: tables[0] = tableName;
0500: } else {
0501: tables[0] = detailTable;
0502: String[] entityInfo = J2EEUtils.findEntity(
0503: mappings, detailTable);
0504: if (entityInfo != null) {
0505: // Detail table exists, make sure to create master table
0506: tables[0] = tableName;
0507: } else {
0508: entityInfo = J2EEUtils.findEntity(mappings,
0509: tableName);
0510: if (entityInfo == null) {
0511: relatedTables = new String[] { tableName };
0512: }
0513: }
0514: }
0515: } else {
0516: if (detailTable == null) {
0517: tables = new String[] { tableName, detailTable };
0518: } else {
0519: tables = new String[] { tableName };
0520: }
0521: }
0522:
0523: for (String table : tables) {
0524: // Find entity that corresponds to the table
0525: String[] entityInfo = J2EEUtils.findEntity(mappings,
0526: table);
0527:
0528: // Create a new entity (if there isn't one that corresponds to the table)
0529: if (entityInfo == null) {
0530: // Generates a Java class for the entity
0531: J2EEUtils.createEntity(folder, scope, unit,
0532: connection, table, relatedTables);
0533:
0534: entityInfo = J2EEUtils.findEntity(mappings, table);
0535: } else {
0536: // Add the entity into the persistence unit if it is not there already
0537: J2EEUtils.addEntityToUnit(entityInfo[1], unit,
0538: project);
0539: }
0540: }
0541:
0542: String[][] result = new String[2][];
0543: result[0] = J2EEUtils.findEntity(mappings, tableName);
0544: if (detailTable != null) {
0545: result[1] = J2EEUtils.findEntity(mappings, detailTable);
0546: if ((result[0] != null) && (result[1] != null)) {
0547: joinInfo = findOneToManyRelationProperties(
0548: mappings, result[0][0], result[1][0],
0549: joinColumn);
0550: if ((joinInfo == null) || joinInfo[1] == null) {
0551: // Determine whether there already exist field mapped to PK column
0552: boolean anotherDetailFieldExists = !J2EEUtils
0553: .propertiesForColumns(
0554: mappings,
0555: result[1][0],
0556: Collections
0557: .singletonList(joinColumn))
0558: .isEmpty();
0559:
0560: // Issue 102630 - missing relationship between entities
0561: addRelationship(
0562: result[0],
0563: result[1],
0564: joinColumn,
0565: referencedColumn,
0566: (joinInfo == null) ? null : joinInfo[0],
0567: anotherDetailFieldExists, folder);
0568: joinInfo = findOneToManyRelationProperties(
0569: mappings, result[0][0], result[1][0],
0570: joinColumn);
0571: }
0572: }
0573: }
0574:
0575: J2EEUtils.makeEntityObservable(folder, result[0], mappings);
0576: if (detailTable != null) {
0577: J2EEUtils.makeEntityObservable(folder, result[1],
0578: mappings);
0579: }
0580:
0581: return result;
0582: } catch (Exception ex) {
0583: Logger.getLogger(getClass().getName()).log(Level.INFO,
0584: ex.getMessage(), ex);
0585: }
0586: return null;
0587: }
0588:
0589: /**
0590: * Uninitializes the wizard.
0591: *
0592: * @param wizard descriptor of the wizard.
0593: */
0594: public void uninitialize(WizardDescriptor wizard) {
0595: if (delegateIterator != null)
0596: delegateIterator.uninitialize(wizard);
0597: }
0598:
0599: /**
0600: * Adds change listener.
0601: *
0602: * @param listener change listener to add.
0603: */
0604: public void addChangeListener(ChangeListener listener) {
0605: // Not used
0606: }
0607:
0608: /**
0609: * Removes change listener.
0610: *
0611: * @param listener change listener to remove.
0612: */
0613: public void removeChangeListener(ChangeListener listener) {
0614: // Not used
0615: }
0616:
0617: private static String[] findOneToManyRelationProperties(
0618: MetadataModel<EntityMappingsMetadata> mappings,
0619: final String masterEntityName,
0620: final String detailEntityName, final String relationColumn)
0621: throws IOException {
0622: String[] properties = null;
0623: try {
0624: properties = mappings
0625: .runReadActionWhenReady(
0626: new MetadataModelAction<EntityMappingsMetadata, String[]>() {
0627: public String[] run(
0628: EntityMappingsMetadata metadata) {
0629: Entity[] entities = metadata
0630: .getRoot().getEntity();
0631: Entity masterEntity = null;
0632: Entity detailEntity = null;
0633: for (int i = 0; i < entities.length; i++) {
0634: String entityName = entities[i]
0635: .getName();
0636: if (masterEntityName
0637: .equals(entityName)) {
0638: masterEntity = entities[i];
0639: }
0640: if (detailEntityName
0641: .equals(entityName)) {
0642: detailEntity = entities[i];
0643: }
0644: }
0645: String relationField = null;
0646: for (ManyToOne manyToOne : detailEntity
0647: .getAttributes()
0648: .getManyToOne()) {
0649: // PENDING when there can be more JoinColumns?
0650: String columnName = manyToOne
0651: .getJoinColumn(0)
0652: .getName();
0653: if (relationColumn
0654: .equals(columnName)) {
0655: relationField = manyToOne
0656: .getName();
0657: break;
0658: }
0659: }
0660: for (OneToMany oneToMany : masterEntity
0661: .getAttributes()
0662: .getOneToMany()) {
0663: String targetEntity = oneToMany
0664: .getTargetEntity();
0665: int index = targetEntity
0666: .lastIndexOf('.');
0667: if (index != -1) {
0668: targetEntity = targetEntity
0669: .substring(index + 1);
0670: }
0671: if (detailEntityName
0672: .equals(targetEntity)
0673: && relationField
0674: .equals(oneToMany
0675: .getMappedBy())) {
0676: return new String[] {
0677: J2EEUtils
0678: .fieldToProperty(relationField),
0679: J2EEUtils
0680: .fieldToProperty(oneToMany
0681: .getName()) };
0682: }
0683: }
0684: if (relationField != null) {
0685: return new String[] {
0686: J2EEUtils
0687: .fieldToProperty(relationField),
0688: null };
0689: } else {
0690: return null;
0691: }
0692: }
0693: }).get();
0694: } catch (InterruptedException iex) {
0695: Logger.getLogger(MasterDetailWizard.class.getName()).log(
0696: Level.INFO, iex.getMessage(), iex);
0697: } catch (ExecutionException eex) {
0698: Logger.getLogger(MasterDetailWizard.class.getName()).log(
0699: Level.INFO, eex.getMessage(), eex);
0700: }
0701: return properties;
0702: }
0703:
0704: private static void addRelationship(final String[] masterInfo,
0705: final String[] detailInfo, final String joinColumn,
0706: final String referencedColumn, String detailField,
0707: final boolean anotherDetailFieldExists,
0708: FileObject fileInProject) {
0709: ClassPath cp = ClassPath.getClassPath(fileInProject,
0710: ClassPath.SOURCE);
0711: final String[] df = new String[] { detailField };
0712: if (df[0] == null) {
0713: String detailJava = detailInfo[1].replace('.', '/')
0714: + ".java"; // NOI18N
0715: FileObject detailFob = cp.findResource(detailJava);
0716: if (detailFob == null)
0717: return;
0718: JavaSource source = JavaSource.forFileObject(detailFob);
0719: try {
0720: source.runModificationTask(
0721: new CancellableTask<WorkingCopy>() {
0722:
0723: public void run(WorkingCopy wc)
0724: throws Exception {
0725: wc.toPhase(JavaSource.Phase.RESOLVED);
0726: CompilationUnitTree cu = wc
0727: .getCompilationUnit();
0728: ClassTree clazz = null;
0729: for (Tree typeDecl : cu.getTypeDecls()) {
0730: if (Tree.Kind.CLASS == typeDecl
0731: .getKind()) {
0732: ClassTree candidate = (ClassTree) typeDecl;
0733: if (candidate
0734: .getModifiers()
0735: .getFlags()
0736: .contains(
0737: javax.lang.model.element.Modifier.PUBLIC)) {
0738: clazz = candidate;
0739: break;
0740: }
0741: }
0742: }
0743:
0744: // Find existing fields and methods
0745: int idx = 0;
0746: int fieldIndex = 0;
0747: Set<String> methods = new HashSet<String>();
0748: Set<String> fields = new HashSet<String>();
0749: for (Tree member : clazz.getMembers()) {
0750: idx++;
0751: if (Tree.Kind.VARIABLE == member
0752: .getKind()) {
0753: VariableTree variable = (VariableTree) member;
0754: fields.add(variable.getName()
0755: .toString());
0756: fieldIndex = idx;
0757: } else if (Tree.Kind.METHOD == member
0758: .getKind()) {
0759: MethodTree method = (MethodTree) member;
0760: methods.add(method.getName()
0761: .toString());
0762: }
0763: }
0764:
0765: // Determine preferred name of the field
0766: StringBuilder sb = new StringBuilder();
0767: boolean upper = false;
0768: for (int i = 0; i < joinColumn.length(); i++) {
0769: char c = joinColumn.charAt(i);
0770: if (c == '_') {
0771: upper = true;
0772: } else {
0773: if (upper) {
0774: c = Character
0775: .toUpperCase(c);
0776: } else {
0777: c = Character
0778: .toLowerCase(c);
0779: }
0780: sb.append(c);
0781: upper = false;
0782: }
0783: }
0784: String detailFieldName = sb.toString();
0785: String detailMethodSuffix = Character
0786: .toUpperCase(detailFieldName
0787: .charAt(0))
0788: + detailFieldName.substring(1);
0789: String candFieldName = detailFieldName;
0790: String candMethodSuffix = detailMethodSuffix;
0791: int count = 1;
0792: boolean ok = false;
0793: while (!ok) {
0794: ok = !fields
0795: .contains(candFieldName);
0796: ok = ok
0797: && !methods.contains("set"
0798: + candMethodSuffix); // NOI18N
0799: ok = ok
0800: && !methods.contains("get"
0801: + candMethodSuffix); // NOI18N
0802: if (!ok) {
0803: count++;
0804: candFieldName = detailFieldName
0805: + count;
0806: candMethodSuffix = detailMethodSuffix
0807: + count;
0808: }
0809: }
0810: detailFieldName = candFieldName;
0811: detailMethodSuffix = candMethodSuffix;
0812: df[0] = detailFieldName;
0813:
0814: // Add the field
0815: TreeMaker make = wc.getTreeMaker();
0816: AssignmentTree nameParameter = make
0817: .Assignment(
0818: make.Identifier("name"),
0819: make
0820: .Literal(joinColumn)); // NOI18N
0821: AssignmentTree referencedParameter = make
0822: .Assignment(
0823: make
0824: .Identifier("referencedColumnName"),
0825: make
0826: .Literal(referencedColumn)); // NOI18N
0827: List<ExpressionTree> parameters = new ArrayList<ExpressionTree>();
0828: parameters.add(nameParameter);
0829: parameters.add(referencedParameter);
0830: if (anotherDetailFieldExists) {
0831: AssignmentTree updatableParameter = make
0832: .Assignment(
0833: make
0834: .Identifier("updatable"),
0835: make
0836: .Identifier("false")); // NOI18N
0837: AssignmentTree insertableParameter = make
0838: .Assignment(
0839: make
0840: .Identifier("insertable"),
0841: make
0842: .Identifier("false")); // NOI18N
0843: parameters.add(updatableParameter);
0844: parameters.add(insertableParameter);
0845: }
0846: TypeElement joinColumnElement = wc
0847: .getElements()
0848: .getTypeElement(
0849: "javax.persistence.JoinColumn"); // NOI18N
0850: AnnotationTree joinColumnTree = make
0851: .Annotation(
0852: make
0853: .QualIdent(joinColumnElement),
0854: parameters);
0855: TypeElement manyToOneElement = wc
0856: .getElements()
0857: .getTypeElement(
0858: "javax.persistence.ManyToOne"); // NOI18N
0859: AnnotationTree manyToOneTree = make
0860: .Annotation(
0861: make
0862: .QualIdent(manyToOneElement),
0863: Collections.EMPTY_LIST); // NOI18N
0864: List<AnnotationTree> annotations = new ArrayList<AnnotationTree>(
0865: 2);
0866: annotations.add(joinColumnTree);
0867: annotations.add(manyToOneTree);
0868: ModifiersTree modifiers = make
0869: .Modifiers(
0870: Collections
0871: .singleton(Modifier.PRIVATE),
0872: annotations);
0873: TypeElement masterElement = wc
0874: .getElements().getTypeElement(
0875: masterInfo[1]);
0876: VariableTree field = make.Variable(
0877: modifiers, detailFieldName,
0878: make.QualIdent(masterElement),
0879: null);
0880: ClassTree modifiedClass = make
0881: .insertClassMember(clazz,
0882: fieldIndex, field);
0883: wc.rewrite(clazz, modifiedClass);
0884:
0885: // Add getter
0886: ReturnTree returnExp = make.Return(make
0887: .Identifier(detailFieldName));
0888: MethodTree getMethod = make
0889: .Method(
0890: make
0891: .Modifiers(
0892: Collections
0893: .singleton(Modifier.PUBLIC),
0894: Collections.EMPTY_LIST),
0895: "get"
0896: + detailMethodSuffix, // NOI18N
0897: make
0898: .QualIdent(masterElement),
0899: Collections.EMPTY_LIST,
0900: Collections.EMPTY_LIST,
0901: Collections.EMPTY_LIST,
0902: make
0903: .Block(
0904: Collections
0905: .singletonList(returnExp),
0906: false),
0907: null);
0908: modifiedClass = make.addClassMember(
0909: modifiedClass, getMethod);
0910: wc.rewrite(clazz, modifiedClass);
0911:
0912: // Add setter
0913: ModifiersTree parMods = make.Modifiers(
0914: Collections.EMPTY_SET,
0915: Collections.EMPTY_LIST);
0916: VariableTree par = make.Variable(
0917: parMods, detailFieldName,
0918: make.QualIdent(masterElement),
0919: null);
0920: AssignmentTree assignExp = make
0921: .Assignment(
0922: make
0923: .Identifier("this."
0924: + detailFieldName),
0925: make
0926: .Identifier(detailFieldName));
0927: MethodTree setMethod = make
0928: .Method(
0929: make
0930: .Modifiers(
0931: Collections
0932: .singleton(Modifier.PUBLIC),
0933: Collections.EMPTY_LIST),
0934: "set"
0935: + detailMethodSuffix, // NOI18N
0936: make
0937: .PrimitiveType(TypeKind.VOID),
0938: Collections.EMPTY_LIST,
0939: Collections
0940: .singletonList(par),
0941: Collections.EMPTY_LIST,
0942: make
0943: .Block(
0944: Collections
0945: .singletonList(make
0946: .ExpressionStatement(assignExp)),
0947: false),
0948: null);
0949: modifiedClass = make.addClassMember(
0950: modifiedClass, setMethod);
0951: wc.rewrite(clazz, modifiedClass);
0952: }
0953:
0954: public void cancel() {
0955: }
0956:
0957: }).commit();
0958: } catch (IOException ioex) {
0959: Logger.getLogger(MasterDetailWizard.class.getName())
0960: .log(Level.INFO, ioex.getMessage(), ioex);
0961: }
0962: }
0963:
0964: String masterJava = masterInfo[1].replace('.', '/') + ".java"; // NOI18N
0965: FileObject masterFob = cp.findResource(masterJava);
0966: if (masterFob == null)
0967: return;
0968: JavaSource source = JavaSource.forFileObject(masterFob);
0969: try {
0970: source.runModificationTask(
0971: new CancellableTask<WorkingCopy>() {
0972:
0973: public void run(WorkingCopy wc)
0974: throws Exception {
0975: wc.toPhase(JavaSource.Phase.RESOLVED);
0976: CompilationUnitTree cu = wc
0977: .getCompilationUnit();
0978: ClassTree clazz = null;
0979: for (Tree typeDecl : cu.getTypeDecls()) {
0980: if (Tree.Kind.CLASS == typeDecl
0981: .getKind()) {
0982: ClassTree candidate = (ClassTree) typeDecl;
0983: if (candidate
0984: .getModifiers()
0985: .getFlags()
0986: .contains(
0987: javax.lang.model.element.Modifier.PUBLIC)) {
0988: clazz = candidate;
0989: break;
0990: }
0991: }
0992: }
0993:
0994: // Find existing fields and methods
0995: int idx = 0;
0996: int fieldIndex = 0;
0997: Set<String> methods = new HashSet<String>();
0998: Set<String> fields = new HashSet<String>();
0999: for (Tree member : clazz.getMembers()) {
1000: idx++;
1001: if (Tree.Kind.VARIABLE == member
1002: .getKind()) {
1003: VariableTree variable = (VariableTree) member;
1004: fields.add(variable.getName()
1005: .toString());
1006: fieldIndex = idx;
1007: } else if (Tree.Kind.METHOD == member
1008: .getKind()) {
1009: MethodTree method = (MethodTree) member;
1010: methods.add(method.getName()
1011: .toString());
1012: }
1013: }
1014:
1015: // Determine preferred name of the field
1016: String masterFieldName = detailInfo[0]
1017: + "Collection"; // NOI18N
1018: masterFieldName = Character
1019: .toLowerCase(masterFieldName
1020: .charAt(0))
1021: + masterFieldName.substring(1);
1022: String masterMethodSuffix = Character
1023: .toUpperCase(masterFieldName
1024: .charAt(0))
1025: + masterFieldName.substring(1);
1026: String candFieldName = masterFieldName;
1027: String candMethodSuffix = masterMethodSuffix;
1028: int count = 1;
1029: boolean ok = false;
1030: while (!ok) {
1031: ok = !fields.contains(candFieldName);
1032: ok = ok
1033: && !methods.contains("set"
1034: + candMethodSuffix); // NOI18N
1035: ok = ok
1036: && !methods.contains("get"
1037: + candMethodSuffix); // NOI18N
1038: if (!ok) {
1039: count++;
1040: candFieldName = masterFieldName
1041: + count;
1042: candMethodSuffix = masterMethodSuffix
1043: + count;
1044: }
1045: }
1046: masterFieldName = candFieldName;
1047: masterMethodSuffix = candMethodSuffix;
1048:
1049: // Add the field
1050: TreeMaker make = wc.getTreeMaker();
1051: TypeElement cascadeTypeElement = wc
1052: .getElements()
1053: .getTypeElement(
1054: "javax.persistence.CascadeType"); // NOI18N
1055: AssignmentTree cascadeParameter = make
1056: .Assignment(
1057: make.Identifier("cascade"),
1058: make
1059: .MemberSelect(
1060: make
1061: .QualIdent(cascadeTypeElement),
1062: "ALL")); // NOI18N
1063: AssignmentTree mappedByParameter = make
1064: .Assignment(make
1065: .Identifier("mappedBy"),
1066: make.Literal(df[0])); // NOI18N
1067: List<ExpressionTree> parameters = new ArrayList<ExpressionTree>();
1068: parameters.add(cascadeParameter);
1069: parameters.add(mappedByParameter);
1070: TypeElement oneToManyElement = wc
1071: .getElements()
1072: .getTypeElement(
1073: "javax.persistence.OneToMany"); // NOI18N
1074: AnnotationTree oneToManyTree = make
1075: .Annotation(
1076: make
1077: .QualIdent(oneToManyElement),
1078: parameters);
1079: ModifiersTree modifiers = make
1080: .Modifiers(
1081: Collections
1082: .singleton(Modifier.PRIVATE),
1083: Collections
1084: .singletonList(oneToManyTree));
1085: TypeElement collectionElement = wc
1086: .getElements().getTypeElement(
1087: "java.util.Collection"); // NOI18N
1088: TypeElement detailElement = wc
1089: .getElements().getTypeElement(
1090: detailInfo[1]);
1091: ParameterizedTypeTree collectionTree = make
1092: .ParameterizedType(
1093: make
1094: .QualIdent(collectionElement),
1095: Collections
1096: .singletonList(make
1097: .QualIdent(detailElement)));
1098: VariableTree field = make.Variable(
1099: modifiers, masterFieldName,
1100: collectionTree, null);
1101: ClassTree modifiedClass = make
1102: .insertClassMember(clazz,
1103: fieldIndex, field);
1104: wc.rewrite(clazz, modifiedClass);
1105:
1106: // Add getter
1107: ReturnTree returnExp = make.Return(make
1108: .Identifier(masterFieldName));
1109: MethodTree getMethod = make
1110: .Method(
1111: make
1112: .Modifiers(
1113: Collections
1114: .singleton(Modifier.PUBLIC),
1115: Collections.EMPTY_LIST),
1116: "get" + masterMethodSuffix, // NOI18N
1117: collectionTree,
1118: Collections.EMPTY_LIST,
1119: Collections.EMPTY_LIST,
1120: Collections.EMPTY_LIST,
1121: make
1122: .Block(
1123: Collections
1124: .singletonList(returnExp),
1125: false),
1126: null);
1127: modifiedClass = make.addClassMember(
1128: modifiedClass, getMethod);
1129: wc.rewrite(clazz, modifiedClass);
1130:
1131: // Add setter
1132: ModifiersTree parMods = make.Modifiers(
1133: Collections.EMPTY_SET,
1134: Collections.EMPTY_LIST);
1135: VariableTree par = make.Variable(parMods,
1136: masterFieldName, collectionTree,
1137: null);
1138: AssignmentTree assignExp = make.Assignment(
1139: make.Identifier("this."
1140: + masterFieldName),
1141: make.Identifier(masterFieldName));
1142: MethodTree setMethod = make
1143: .Method(
1144: make
1145: .Modifiers(
1146: Collections
1147: .singleton(Modifier.PUBLIC),
1148: Collections.EMPTY_LIST),
1149: "set" + masterMethodSuffix, // NOI18N
1150: make
1151: .PrimitiveType(TypeKind.VOID),
1152: Collections.EMPTY_LIST,
1153: Collections
1154: .singletonList(par),
1155: Collections.EMPTY_LIST,
1156: make
1157: .Block(
1158: Collections
1159: .singletonList(make
1160: .ExpressionStatement(assignExp)),
1161: false),
1162: null);
1163: modifiedClass = make.addClassMember(
1164: modifiedClass, setMethod);
1165: wc.rewrite(clazz, modifiedClass);
1166: }
1167:
1168: public void cancel() {
1169: }
1170:
1171: }).commit();
1172: } catch (IOException ioex) {
1173: Logger.getLogger(MasterDetailWizard.class.getName()).log(
1174: Level.INFO, ioex.getMessage(), ioex);
1175: }
1176: }
1177:
1178: }
|