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: package org.netbeans.modules.form.j2ee;
0042:
0043: import com.sun.source.tree.*;
0044: import java.awt.Component;
0045: import java.awt.EventQueue;
0046: import java.io.IOException;
0047: import java.lang.reflect.Modifier;
0048: import java.net.URL;
0049: import java.sql.Connection;
0050: import java.sql.ResultSet;
0051: import java.util.ArrayList;
0052: import java.util.Arrays;
0053: import java.util.*;
0054: import java.util.concurrent.ExecutionException;
0055: import java.util.logging.Level;
0056: import java.util.logging.Logger;
0057: import javax.lang.model.element.Element;
0058: import javax.lang.model.element.ExecutableElement;
0059: import javax.lang.model.element.TypeElement;
0060: import javax.lang.model.element.VariableElement;
0061: import javax.lang.model.type.TypeKind;
0062: import javax.lang.model.type.TypeMirror;
0063: import javax.lang.model.util.ElementFilter;
0064: import javax.swing.DefaultListCellRenderer;
0065: import javax.swing.JList;
0066: import javax.swing.ListCellRenderer;
0067: import org.netbeans.api.db.explorer.ConnectionManager;
0068: import org.netbeans.api.db.explorer.DatabaseConnection;
0069: import org.netbeans.api.db.explorer.JDBCDriver;
0070: import org.netbeans.api.java.classpath.ClassPath;
0071: import org.netbeans.api.java.project.JavaProjectConstants;
0072: import org.netbeans.api.java.queries.UnitTestForSourceQuery;
0073: import org.netbeans.api.java.source.CancellableTask;
0074: import org.netbeans.api.java.source.CompilationController;
0075: import org.netbeans.api.java.source.JavaSource;
0076: import org.netbeans.api.java.source.TreeMaker;
0077: import org.netbeans.api.java.source.WorkingCopy;
0078: import org.netbeans.api.progress.aggregate.AggregateProgressFactory;
0079: import org.netbeans.api.project.FileOwnerQuery;
0080: import org.netbeans.api.project.Project;
0081: import org.netbeans.api.project.ProjectUtils;
0082: import org.netbeans.api.project.SourceGroup;
0083: import org.netbeans.api.project.ui.OpenProjects;
0084: import org.netbeans.api.project.libraries.LibraryManager;
0085: import org.netbeans.modules.form.FormEditor;
0086: import org.netbeans.modules.form.FormModel;
0087: import org.netbeans.modules.form.FormProperty;
0088: import org.netbeans.modules.form.FormUtils;
0089: import org.netbeans.modules.form.RADComponent;
0090: import org.netbeans.modules.form.codestructure.CodeStructure;
0091: import org.netbeans.modules.form.project.ClassPathUtils;
0092: import org.netbeans.modules.form.project.ClassSource;
0093: import org.netbeans.modules.j2ee.metadata.model.api.MetadataModel;
0094: import org.netbeans.modules.j2ee.metadata.model.api.MetadataModelAction;
0095: import org.netbeans.modules.j2ee.persistence.api.PersistenceScope;
0096: import org.netbeans.modules.j2ee.persistence.api.entity.generator.EntitiesFromDBGenerator;
0097: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Attributes;
0098: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Basic;
0099: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Entity;
0100: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.EntityMappingsMetadata;
0101: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Id;
0102: import org.netbeans.modules.j2ee.persistence.api.metadata.orm.ManyToOne;
0103: import org.netbeans.modules.j2ee.persistence.dd.PersistenceMetadata;
0104: import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.Persistence;
0105: import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.PersistenceUnit;
0106: import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.Property;
0107: import org.netbeans.modules.j2ee.persistence.provider.InvalidPersistenceXmlException;
0108: import org.netbeans.modules.j2ee.persistence.provider.Provider;
0109: import org.netbeans.modules.j2ee.persistence.provider.ProviderUtil;
0110: import org.netbeans.modules.j2ee.persistence.wizard.fromdb.*;
0111: import org.openide.filesystems.FileObject;
0112: import org.openide.filesystems.FileUtil;
0113: import org.openide.filesystems.URLMapper;
0114: import org.openide.util.NbBundle;
0115: import org.openide.util.Parameters;
0116: import org.openide.util.Utilities;
0117:
0118: /**
0119: * Utility methods.
0120: *
0121: * @author Jan Stola
0122: */
0123: public class J2EEUtils {
0124: /** Determines whether related entities should be generated. */
0125: public static final boolean TABLE_CLOSURE = true;
0126:
0127: // Utility class - no need to instantiate
0128: private J2EEUtils() {
0129: }
0130:
0131: /**
0132: * Returns persistence unit that corresponds to given database URL.
0133: *
0134: * @param persistence persistence context.
0135: * @param dbURL database URL.
0136: * @return persistence unit that corresponds to given database URL.
0137: */
0138: public static PersistenceUnit findPersistenceUnit(
0139: Persistence persistence, String dbURL) {
0140: for (PersistenceUnit unit : persistence.getPersistenceUnit()) {
0141: Provider provider = ProviderUtil.getProvider(unit);
0142: String unitURL = ProviderUtil.getProperty(unit,
0143: provider.getJdbcUrl()).getValue();
0144: if (dbURL.equals(unitURL)) {
0145: return unit;
0146: }
0147: }
0148: return null;
0149: }
0150:
0151: /**
0152: * Creates persistence unit according to given DB <code>connection</code>.
0153: *
0154: * @param project target project for the new persistence unit.
0155: * @param connection database connection.
0156: * @throws IOException when something goes wrong.
0157: * @throws InvalidPersistenceXmlException
0158: * @return persistence unit that corresponds to given DB <code>connection</code>.
0159: */
0160: public static PersistenceUnit createPersistenceUnit(
0161: Project project, DatabaseConnection connection)
0162: throws IOException, InvalidPersistenceXmlException {
0163: FileObject persistenceXML = ProviderUtil.getDDFile(project);
0164: Persistence persistence = PersistenceMetadata.getDefault()
0165: .getRoot(persistenceXML);
0166: String dbURL = connection.getDatabaseURL();
0167:
0168: // Determine name of the PU
0169: String dbName = dbURL.substring(dbURL.lastIndexOf('/') + 1);
0170: String puName = dbName + "PU"; // NOI18N
0171: PersistenceUnit unit = findPersistenceUnit(persistence, puName);
0172: int count = 0;
0173: while (unit != null) {
0174: count++;
0175: puName = dbName + "PU" + count; // NOI18N
0176: unit = findPersistenceUnit(persistence, puName);
0177: }
0178:
0179: // Determine the provider
0180: Provider provider;
0181: if (persistence.getPersistenceUnit().length > 0) {
0182: // Use the same provider as the existing persistence unit
0183: provider = ProviderUtil.getProvider(persistence
0184: .getPersistenceUnit(0));
0185: } else {
0186: // The first persistence unit - use TopLink provider
0187: // (it is delivered as a part of NetBeans J2EE support)
0188: provider = ProviderUtil.TOPLINK_PROVIDER;
0189: }
0190:
0191: unit = ProviderUtil.buildPersistenceUnit(puName, provider,
0192: connection);
0193: unit.setTransactionType("RESOURCE_LOCAL"); // NOI18N
0194:
0195: // TopLink/Derby combination doesn't like empty username and password,
0196: // but we can use dummy (app/app) values in this case, see issue 121427.
0197: if ((nullOrEmpty(connection.getUser()) || nullOrEmpty(connection
0198: .getPassword()))
0199: && ProviderUtil.TOPLINK_PROVIDER.equals(provider)
0200: && connection.getDriverClass().startsWith(
0201: "org.apache.derby.jdbc.")) { // NOI18N
0202: String userPropName = provider.getJdbcUsername();
0203: String passwdPropName = provider.getJdbcPassword();
0204: for (Property prop : unit.getProperties().getProperty2()) {
0205: String propName = prop.getName();
0206: if ((userPropName.equals(propName) || passwdPropName
0207: .equals(propName))
0208: && nullOrEmpty(prop.getValue())) {
0209: prop.setValue("app"); // NOI18N
0210: }
0211: }
0212: }
0213:
0214: // Java Embedded DB, see issue 121391
0215: if ("org.apache.derby.jdbc.EmbeddedDriver".equals(connection
0216: .getDriverClass())) { // NOI18N
0217: // Make sure tables are created if they do not exist
0218: ProviderUtil.setTableGeneration(unit,
0219: Provider.TABLE_GENERATION_CREATE, provider);
0220: // Make sure the DB is created if it does not exist
0221: if (!dbURL.contains(";create=true")) { // NOI18N
0222: if (!dbURL.endsWith(";")) { // NOI18N
0223: dbURL += ";"; // NOI18N
0224: }
0225: dbURL += "create=true"; // NOI18N
0226: Property prop = ProviderUtil.getProperty(unit, provider
0227: .getJdbcUrl());
0228: if (prop != null) {
0229: prop.setValue(dbURL);
0230: }
0231: }
0232: }
0233: ProviderUtil.addPersistenceUnit(unit, project);
0234:
0235: return unit;
0236: }
0237:
0238: public static boolean nullOrEmpty(String s) {
0239: return (s == null) || "".equals(s); // NOI18N
0240: }
0241:
0242: /**
0243: * Returns names of persistence units in the specified project.
0244: *
0245: * @param project project to scan for persistence units.
0246: * @return names of persistence units in the specified project.
0247: */
0248: public static String[] getPersistenceUnitNames(Project project) {
0249: FileObject persistenceXML;
0250: try {
0251: persistenceXML = J2EEUtils
0252: .getPersistenceXML(project, false);
0253: } catch (InvalidPersistenceXmlException ipxex) {
0254: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0255: ipxex.getMessage(), ipxex);
0256: return new String[0];
0257: }
0258: if (persistenceXML == null)
0259: return new String[0];
0260: Persistence persistence = null;
0261: try {
0262: persistence = PersistenceMetadata.getDefault().getRoot(
0263: persistenceXML);
0264: } catch (IOException ioex) {
0265: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0266: ioex.getMessage(), ioex);
0267: return new String[0];
0268: }
0269: PersistenceUnit[] unit = persistence.getPersistenceUnit();
0270: String[] names = new String[unit.length];
0271: for (int i = 0; i < unit.length; i++) {
0272: names[i] = unit[i].getName();
0273: }
0274: return names;
0275: }
0276:
0277: public static void addEntityToUnit(String entityClass,
0278: PersistenceUnit unit, Project project) {
0279: boolean added = false;
0280: for (String clazz : unit.getClass2()) {
0281: if (entityClass.equals(clazz)) {
0282: added = true;
0283: break;
0284: }
0285: }
0286: if (!added) {
0287: try {
0288: ProviderUtil.addManagedClass(unit, entityClass,
0289: ProviderUtil.getPUDataObject(project));
0290: } catch (InvalidPersistenceXmlException ipxex) {
0291: Logger.getLogger(J2EEUtils.class.getName()).log(
0292: Level.INFO, ipxex.getMessage(), ipxex);
0293: }
0294: }
0295: }
0296:
0297: /**
0298: * Updates project classpath with the TopLink library.
0299: *
0300: * @param fileInProject file in the project whose classpath should be updated.
0301: * @return <code>true</code> if the classpath has been updated,
0302: * returns <code>false</code> otherwise.
0303: */
0304: public static boolean updateProjectForTopLink(
0305: FileObject fileInProject) {
0306: try {
0307: ClassPath classPath = ClassPath.getClassPath(fileInProject,
0308: ClassPath.EXECUTE);
0309: FileObject fob = classPath
0310: .findResource("oracle/toplink/essentials/ejb/cmp3/EntityManagerFactoryProvider.class"); // NOI18N
0311: if (fob == null) {
0312: ClassSource cs = new ClassSource("", // class name is not needed // NOI18N
0313: new ClassSource.LibraryEntry(LibraryManager
0314: .getDefault().getLibrary("toplink"))); // NOI18N
0315: return ClassPathUtils.updateProject(fileInProject, cs);
0316: }
0317: } catch (IOException ex) {
0318: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0319: ex.getMessage(), ex);
0320: }
0321: return false;
0322: }
0323:
0324: /**
0325: * Updates project classpath with the JARs specified by the <code>urls</code>.
0326: * The classpath is not updated if the given reference class (<code>refClassName</code>)
0327: * is on the classpath.
0328: *
0329: * @param urls URLs of the JARs that should be added to the classpath.
0330: * @param refClassName name of the class that determines wheter the classpath
0331: * should be updated or not.
0332: * @param fileInProject file in the project whose classpath should be updated.
0333: * @return <code>true</code> if the classpath has been updated,
0334: * returns <code>false</code> otherwise.
0335: */
0336: public static boolean updateProjectWithJARs(URL[] urls,
0337: String refClassName, FileObject fileInProject) {
0338: try {
0339: ClassPath classPath = ClassPath.getClassPath(fileInProject,
0340: ClassPath.EXECUTE);
0341: String resourceName = refClassName.replace('.', '/')
0342: + ".class"; // NOI18N
0343: FileObject fob = classPath.findResource(resourceName); // NOI18N
0344: if (fob == null) {
0345: List<ClassSource.Entry> cpEntries = new ArrayList<ClassSource.Entry>(
0346: urls.length);
0347: for (URL url : urls) {
0348: cpEntries.add(new ClassSource.JarEntry(FileUtil
0349: .toFile(URLMapper.findFileObject(url))));
0350: }
0351: return ClassPathUtils.updateProject(fileInProject,
0352: new ClassSource("", cpEntries)); // NOI18N
0353: }
0354: } catch (IOException ex) {
0355: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0356: ex.getMessage(), ex);
0357: }
0358: return false;
0359: }
0360:
0361: /**
0362: * Returns persistence descriptor for the given project.
0363: *
0364: * @param project project where the persistence descriptor should be found.
0365: * @param create determines whether the persistence descriptor should be created
0366: * (if there is not one already)
0367: * @throws InvalidPersistenceXmlException
0368: * @return persistence descriptor for the given project.
0369: */
0370: public static FileObject getPersistenceXML(Project project,
0371: boolean create) throws InvalidPersistenceXmlException {
0372: FileObject persistenceXML = ProviderUtil.getDDFile(project);
0373: if ((persistenceXML == null) && create) {
0374: // Forces creation of persistence.xml
0375: ProviderUtil.getPUDataObject(project);
0376: persistenceXML = ProviderUtil.getDDFile(project);
0377: }
0378: return persistenceXML;
0379: }
0380:
0381: /**
0382: * Returns entity manager RAD component that corresponds to the specified persistence unit.
0383: *
0384: * @param model form model where the RAD component should be found.
0385: * @param puName name of the persistence unit.
0386: * @return entity manager RAD component that corresponds to the specified persistence unit
0387: * or <code>null</code> if such entity manager is not in the given form model.
0388: */
0389: public static RADComponent findEntityManager(FormModel model,
0390: String puName) {
0391: for (RADComponent metacomp : model.getAllComponents()) {
0392: if ("javax.persistence.EntityManager".equals(metacomp
0393: .getBeanClass().getName())) { // NOI18N
0394: try {
0395: FormProperty prop = (FormProperty) metacomp
0396: .getPropertyByName("persistenceUnit"); // NOI18N
0397: Object name = prop.getRealValue();
0398: if (puName.equals(name)) {
0399: return metacomp;
0400: }
0401: } catch (Exception ex) {
0402: Logger.getLogger(J2EEUtils.class.getName()).log(
0403: Level.INFO, ex.getMessage(), ex);
0404: }
0405: }
0406: }
0407: return null;
0408: }
0409:
0410: /**
0411: * Creates entity manager RAD component that corresponds to the specified persistence unit.
0412: *
0413: * @param model form model where the RAD component should be created.
0414: * @param puName name of the persistence unit.
0415: * @return entity manager RAD component that corresponds to the specified persistence unit.
0416: * @throws Exception when something goes wrong ;-).
0417: */
0418: public static RADComponent createEntityManager(FormModel model,
0419: String puName) throws Exception {
0420: assert EventQueue.isDispatchThread();
0421: FileObject formFile = FormEditor.getFormDataObject(model)
0422: .getFormFile();
0423: Class<?> emClass = ClassPathUtils.loadClass(
0424: "javax.persistence.EntityManager", formFile); // NOI18N
0425: RADComponent entityManager = new RADComponent();
0426: entityManager.initialize(model);
0427: entityManager.initInstance(emClass);
0428: entityManager.getPropertyByName("persistenceUnit").setValue(
0429: puName); // NOI18N
0430: renameComponent(entityManager, false, puName + "EntityManager",
0431: "entityManager"); // NOI18N
0432: model.addComponent(entityManager, null, true);
0433: return entityManager;
0434: }
0435:
0436: /**
0437: * Returns entity in the specified persistence unit that corresponds to the given table.
0438: *
0439: * @param mappings entity mapping information.
0440: * @param tableName name of the table for the searched entity.
0441: * @throws IOException when something goes wrong
0442: * @return entity in the specified persistence unit that corresponds to the given table
0443: * or <code>null</code> if such an entity doesn't exist.
0444: */
0445: public static String[] findEntity(
0446: MetadataModel<EntityMappingsMetadata> mappings,
0447: final String tableName) throws IOException {
0448: String[] entity = null;
0449: try {
0450: entity = mappings
0451: .runReadActionWhenReady(
0452: new MetadataModelAction<EntityMappingsMetadata, String[]>() {
0453: public String[] run(
0454: EntityMappingsMetadata metadata) {
0455: Entity[] entity = metadata
0456: .getRoot().getEntity();
0457: for (int i = 0; i < entity.length; i++) {
0458: String name = entity[i]
0459: .getTable().getName();
0460: name = unquote(name);
0461: if (tableName.equals(name)) {
0462: return new String[] {
0463: entity[i].getName(),
0464: entity[i]
0465: .getClass2() };
0466: }
0467: }
0468: return null;
0469: }
0470: }).get();
0471: } catch (InterruptedException iex) {
0472: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0473: iex.getMessage(), iex);
0474: } catch (ExecutionException eex) {
0475: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0476: eex.getMessage(), eex);
0477: }
0478: return entity;
0479: }
0480:
0481: public static String unquote(String name) {
0482: while (name.startsWith("\"") && name.endsWith("\"")) { // NOI18N
0483: name = name.substring(1, name.length() - 1);
0484: }
0485: return name;
0486: }
0487:
0488: /**
0489: * Generates entity classes for given tables.
0490: *
0491: * @param project project where the classes should be generated.
0492: * @param location source location.
0493: * @param packageName name of the package where the classes should be generated.
0494: * @param tableNames names of the tables.
0495: * @param dbconn connection to the DB with the tables.
0496: * @param unit persistence unit to add the generated classes into
0497: * @return entity class that corresponds to the specified table and names
0498: * of the entity classes for related tables (if <code>relatedTableNames</code>
0499: * parameter was non-<code>null</code>).
0500: */
0501: private static String[] generateEntityClass(final Project project,
0502: SourceGroup location, String packageName,
0503: DatabaseConnection dbconn, List<String> tableNames,
0504: PersistenceUnit unit) {
0505: try {
0506: EntitiesFromDBGenerator generator = new EntitiesFromDBGenerator(
0507: tableNames, true, packageName, location, dbconn,
0508: project, unit);
0509: // PENDING
0510: final Set<FileObject> entities = generator
0511: .generate(AggregateProgressFactory
0512: .createProgressContributor("PENDING"));
0513:
0514: String[] result = new String[entities.size()];
0515: int count = 0;
0516: for (FileObject fob : entities) {
0517: addSchemaParameter(fob, dbconn.getSchema()); // see issue 121493 and 89092
0518: result[count++] = packageName + '.' + fob.getName();
0519: }
0520: return result;
0521: } catch (Exception ex) {
0522: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0523: ex.getMessage(), ex);
0524: }
0525: return null;
0526: }
0527:
0528: /**
0529: * Makes sure that the given database connection is established.
0530: *
0531: * @param connection connection that should be established.
0532: * @return established connection, may return <code>null</code> if the user cancel
0533: * the connection dialog.
0534: */
0535: public static Connection establishConnection(
0536: DatabaseConnection connection) {
0537: Connection con = connection.getJDBCConnection();
0538: if (con == null) { // connection not established yet
0539: ConnectionManager.getDefault().showConnectionDialog(
0540: connection);
0541: con = connection.getJDBCConnection();
0542: }
0543: return con;
0544: }
0545:
0546: /**
0547: * Updates project's classpath if necessary.
0548: *
0549: * @param fileInProject file in the project whose classpath should be updated.
0550: * @param unit persistence unit to be used in the project.
0551: * @param driver JDBC driver to be used in the project.
0552: */
0553: public static void updateProjectForUnit(FileObject fileInProject,
0554: PersistenceUnit unit, JDBCDriver driver) {
0555: // Make sure that TopLink JAR files are on the classpath (if using TopLink)
0556: if (ProviderUtil.TOPLINK_PROVIDER.equals(ProviderUtil
0557: .getProvider(unit))) {
0558: updateProjectForTopLink(fileInProject);
0559: }
0560:
0561: // Make sure that DB driver classes are on the classpath
0562: updateProjectWithJARs(driver.getURLs(), driver.getClassName(),
0563: fileInProject);
0564: }
0565:
0566: /**
0567: * Initializes persistence unit and persistence descriptor.
0568: *
0569: * @param persistenceXML persistence descriptor.
0570: * @param connection DB connection that specifies parameters of the persistence unit.
0571: * @return persistence unit that corresponds to the given DB connection.
0572: * @throws IOException if there is a problem with creation of the persistence unit.
0573: * @throws InvalidPersistenceXmlException
0574: */
0575: public static PersistenceUnit initPersistenceUnit(
0576: FileObject persistenceXML, DatabaseConnection connection)
0577: throws IOException, InvalidPersistenceXmlException {
0578: Project project = FileOwnerQuery.getOwner(persistenceXML);
0579:
0580: // Make sure the database connection is established
0581: J2EEUtils.establishConnection(connection);
0582:
0583: // Make sure there is a persistence unit that corresponds to our DB connection
0584: Persistence persistence = PersistenceMetadata.getDefault()
0585: .getRoot(persistenceXML);
0586: PersistenceUnit unit = J2EEUtils.findPersistenceUnit(
0587: persistence, connection.getDatabaseURL());
0588: if (unit == null) {
0589: unit = J2EEUtils.createPersistenceUnit(project, connection);
0590: }
0591: return unit;
0592: }
0593:
0594: /**
0595: * Creates entity that corresponds to the specified table (accessible via given DB connection).
0596: * Possibly creates also entities for related tables.
0597: *
0598: * @param dir directory where the entity should be created.
0599: * @param scope persistence scope where the entity should be created.
0600: * @param unit persistence unit where the entity should be created.
0601: * @param connection connection through which the table is accessible.
0602: * @param tableName name of the table.
0603: * @param relatedTableNames names of related tables whose entity classes should be added
0604: * into the peristence unit.
0605: * @throws Exception when something goes wrong.
0606: */
0607: public static void createEntity(FileObject dir,
0608: PersistenceScope scope, PersistenceUnit unit,
0609: DatabaseConnection connection, String tableName,
0610: String[] relatedTableNames) throws Exception {
0611: Project project = FileOwnerQuery.getOwner(dir);
0612: String packageName = scope.getClassPath().getResourceName(dir,
0613: '.', false);
0614:
0615: SourceGroup[] groups = getJavaSourceGroups(project);
0616: SourceGroup location = groups[0];
0617: for (int i = 0; i < groups.length; i++) {
0618: if (groups[i].contains(dir)) {
0619: location = groups[i];
0620: break;
0621: }
0622: }
0623: List<String> tableNames = new LinkedList<String>();
0624: tableNames.add(tableName);
0625: if (relatedTableNames != null) {
0626: List<String> relatedTables = Arrays
0627: .asList(relatedTableNames);
0628: if (relatedTables.contains(tableName)) {
0629: tableNames.remove(tableName);
0630: }
0631: tableNames.addAll(relatedTables);
0632: }
0633: J2EEUtils.generateEntityClass(project, location, packageName,
0634: connection, tableNames, unit);
0635: // PENDING ugly workaround for the fact that the generated entity is not immediately
0636: // in the model - will be removed as soon as the corresponding issue is fixed
0637: try {
0638: outer: for (int i = 0; i < 30; i++) {
0639: MetadataModel<EntityMappingsMetadata> mappings = scope
0640: .getEntityMappingsModel(unit.getName());
0641: for (String table : tableNames) {
0642: String[] entityInfo = J2EEUtils.findEntity(
0643: mappings, table);
0644: if (entityInfo == null) {
0645: Thread.sleep(1000);
0646: continue outer;
0647: }
0648: }
0649: break;
0650: }
0651: } catch (InterruptedException iex) {
0652: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0653: iex.getMessage(), iex);
0654: }
0655: }
0656:
0657: public static String fieldToProperty(String fieldName) {
0658: char first = fieldName.charAt(0);
0659: if (fieldName.length() > 1) {
0660: char second = fieldName.charAt(1);
0661: String suffix = fieldName.substring(1);
0662: if (Character.isLowerCase(second)) {
0663: first = Character.toLowerCase(first);
0664: } else {
0665: first = Character.toUpperCase(first);
0666: }
0667: return first + suffix;
0668: } else {
0669: return Character.toString(Character.toLowerCase(first));
0670: }
0671: }
0672:
0673: /**
0674: * Renames metacomponent.
0675: *
0676: * @param comp component to rename.
0677: * @param inModel determines whether the component is already in the model.
0678: * @param name suggested new names for the component.
0679: */
0680: public static void renameComponent(RADComponent comp,
0681: boolean inModel, String... name) {
0682: String oldName = comp.getName();
0683: FormModel formModel = comp.getFormModel();
0684: int index = 0;
0685: while (!Utilities.isJavaIdentifier(name[index]))
0686: index++;
0687: String prefix = name[index];
0688: String newName;
0689: CodeStructure codeStructure = formModel.getCodeStructure();
0690: if (codeStructure.isVariableNameReserved(prefix)
0691: && !prefix.equals(oldName)) {
0692: index = 0;
0693: while (codeStructure.isVariableNameReserved(prefix + index)
0694: && !prefix.equals(oldName))
0695: index++;
0696: newName = prefix + index;
0697: } else {
0698: newName = prefix;
0699: }
0700: if (inModel) {
0701: comp.setName(newName);
0702: } else {
0703: comp.setStoredName(newName);
0704: }
0705: }
0706:
0707: public static boolean hasPrimaryKey(DatabaseConnection connection,
0708: String tableName) {
0709: Connection con = connection.getJDBCConnection();
0710: boolean hasPK = false;
0711: try {
0712: ResultSet rs = con.getMetaData()
0713: .getPrimaryKeys(con.getCatalog(),
0714: connection.getSchema(), tableName);
0715: hasPK = rs.next();
0716: rs.close();
0717: } catch (Exception ex) {
0718: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0719: ex.getMessage(), ex);
0720: }
0721: return hasPK;
0722: }
0723:
0724: /**
0725: * Finds out tables in the DB represented by the given DB connection.
0726: *
0727: * @param connection DB connection to search for tables.
0728: * @return list of names of the tables.
0729: */
0730: public static List<DBColumnInfo> tableNamesForConnection(
0731: DatabaseConnection connection) {
0732: Connection con = connection.getJDBCConnection();
0733: List<DBColumnInfo> tables = new LinkedList<DBColumnInfo>();
0734: try {
0735: ResultSet rs = con.getMetaData().getTables(
0736: con.getCatalog(), connection.getSchema(), "%",
0737: new String[] { "TABLE" }); // NOI18N
0738: while (rs.next()) {
0739: String tableName = rs.getString("TABLE_NAME"); // NOI18N
0740: boolean hasPK = hasPrimaryKey(connection, tableName);
0741: tables.add(new DBColumnInfo(tableName, hasPK,
0742: hasPK ? null : NbBundle.getMessage(
0743: J2EEUtils.class, "MSG_NO_PK"))); // NOI18N
0744: }
0745: rs.close();
0746: } catch (Exception ex) {
0747: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
0748: ex.getMessage(), ex);
0749: }
0750: return tables;
0751: }
0752:
0753: public static class DBColumnInfo {
0754: private String name;
0755: private boolean valid;
0756: private String message;
0757:
0758: DBColumnInfo(String name, boolean valid, String message) {
0759: this .name = name;
0760: this .valid = valid;
0761: this .message = message;
0762: }
0763:
0764: public String getName() {
0765: return name;
0766: }
0767:
0768: public boolean isValid() {
0769: return valid;
0770: }
0771:
0772: public String getMessage() {
0773: return message;
0774: }
0775:
0776: public static ListCellRenderer getRenderer() {
0777: return new DefaultListCellRenderer() {
0778: @Override
0779: public Component getListCellRendererComponent(
0780: JList list, Object value, int index,
0781: boolean isSelected, boolean cellHasFocus) {
0782: super .getListCellRendererComponent(list, value,
0783: index, isSelected, cellHasFocus);
0784: if (value instanceof DBColumnInfo) {
0785: DBColumnInfo column = (DBColumnInfo) value;
0786: String label = column.getName()
0787: + (column.isValid() ? "" : " ("
0788: + column.getMessage() + ")"); // NOI18N
0789: setText(label);
0790: setEnabled(column.isValid());
0791: }
0792: return this ;
0793: }
0794: };
0795: }
0796: }
0797:
0798: /**
0799: * Makes the entity observable (e.g. adds property change support).
0800: *
0801: * @param entity entity to make observable.
0802: */
0803: public static void makeEntityObservable(FileObject fileInProject,
0804: String[] entityInfo,
0805: MetadataModel<EntityMappingsMetadata> mappings) {
0806: if (entityInfo == null)
0807: return;
0808: ClassPath cp = ClassPath.getClassPath(fileInProject,
0809: ClassPath.SOURCE);
0810: String resName = entityInfo[1].replace('.', '/') + ".java"; // NOI18N
0811: FileObject entity = cp.findResource(resName);
0812: if (entity == null)
0813: return;
0814: final List<String> properties;
0815: try {
0816: properties = propertiesForColumns(mappings, entityInfo[0],
0817: null);
0818: } catch (IOException ioex) {
0819: return;
0820: }
0821: JavaSource source = JavaSource.forFileObject(entity);
0822: final boolean[] alreadyUpdated = new boolean[1];
0823: try {
0824: // PENDING merge into one task once it will be possible
0825: source.runModificationTask(
0826: new CancellableTask<WorkingCopy>() {
0827:
0828: public void run(WorkingCopy wc)
0829: throws Exception {
0830: wc.toPhase(JavaSource.Phase.RESOLVED);
0831: CompilationUnitTree cu = wc
0832: .getCompilationUnit();
0833: ClassTree clazz = null;
0834: for (Tree typeDecl : cu.getTypeDecls()) {
0835: if (Tree.Kind.CLASS == typeDecl
0836: .getKind()) {
0837: ClassTree candidate = (ClassTree) typeDecl;
0838: if (candidate
0839: .getModifiers()
0840: .getFlags()
0841: .contains(
0842: javax.lang.model.element.Modifier.PUBLIC)) {
0843: clazz = candidate;
0844: break;
0845: }
0846: }
0847: }
0848:
0849: for (Tree member : clazz.getMembers()) {
0850: if (Tree.Kind.VARIABLE == member
0851: .getKind()) {
0852: VariableTree variable = (VariableTree) member;
0853: String type = variable.getType()
0854: .toString();
0855: if (type
0856: .endsWith("PropertyChangeSupport")) { // NOI18N
0857: alreadyUpdated[0] = true;
0858: }
0859: }
0860: }
0861:
0862: TreeMaker make = wc.getTreeMaker();
0863: ClassTree modifiedClass = clazz;
0864:
0865: if (!alreadyUpdated[0]) {
0866: // changeSupport field
0867: TypeElement transientElement = wc
0868: .getElements()
0869: .getTypeElement(
0870: "javax.persistence.Transient"); // NOI18N
0871: TypeMirror transientMirror = transientElement
0872: .asType();
0873: Tree transientType = make
0874: .Type(transientMirror);
0875: AnnotationTree transientTree = make
0876: .Annotation(transientType,
0877: Collections.EMPTY_LIST);
0878: ModifiersTree modifiers = make
0879: .Modifiers(
0880: Modifier.PRIVATE,
0881: Collections
0882: .singletonList(transientTree));
0883: TypeElement changeSupportElement = wc
0884: .getElements()
0885: .getTypeElement(
0886: "java.beans.PropertyChangeSupport"); // NOI18N
0887: TypeMirror changeSupportMirror = changeSupportElement
0888: .asType();
0889: Tree changeSupportType = make
0890: .Type(changeSupportMirror);
0891: NewClassTree changeSupportConstructor = make
0892: .NewClass(
0893: null,
0894: Collections.EMPTY_LIST,
0895: make
0896: .QualIdent(changeSupportElement),
0897: Collections
0898: .singletonList(make
0899: .Identifier("this")),
0900: null);
0901: VariableTree changeSupport = make
0902: .Variable(modifiers,
0903: "changeSupport",
0904: changeSupportType,
0905: changeSupportConstructor); // NOI18N
0906: modifiedClass = make.insertClassMember(
0907: clazz, 0, changeSupport);
0908: }
0909:
0910: // property change notification
0911: for (Tree clMember : modifiedClass
0912: .getMembers()) {
0913: if (clMember.getKind() == Tree.Kind.METHOD) {
0914: MethodTree method = (MethodTree) clMember;
0915: String methodName = method
0916: .getName().toString();
0917: if (methodName.startsWith("set")
0918: && (methodName.length() > 3)
0919: && (Character
0920: .isUpperCase(methodName
0921: .charAt(3)))
0922: && (method.getParameters()
0923: .size() == 1)) { // NOI18N
0924: String propName = methodName
0925: .substring(3);
0926: if ((propName.length() == 1)
0927: || (Character
0928: .isLowerCase(propName
0929: .charAt(1)))) {
0930: propName = Character
0931: .toLowerCase(propName
0932: .charAt(0))
0933: + propName
0934: .substring(1);
0935: }
0936: if (!properties
0937: .contains(propName))
0938: continue;
0939: BlockTree block = method
0940: .getBody();
0941: if (block.getStatements()
0942: .size() != 1)
0943: continue;
0944: StatementTree statement = block
0945: .getStatements().get(0);
0946: if (statement.getKind() != Tree.Kind.EXPRESSION_STATEMENT)
0947: continue;
0948: ExpressionTree expression = ((ExpressionStatementTree) statement)
0949: .getExpression();
0950: if (expression.getKind() != Tree.Kind.ASSIGNMENT)
0951: continue;
0952: AssignmentTree assignment = (AssignmentTree) expression;
0953: String parName = assignment
0954: .getExpression()
0955: .toString();
0956: VariableTree parameter = method
0957: .getParameters().get(0);
0958: if (!parameter.getName()
0959: .toString().equals(
0960: parName))
0961: continue;
0962: ExpressionTree persistentVariable = assignment
0963: .getVariable();
0964:
0965: // <Type> old<PropertyName> = this.<propertyName>
0966: String parameterName = parameter
0967: .getName().toString();
0968: String oldParameterName = "old"
0969: + Character
0970: .toUpperCase(parameterName
0971: .charAt(0))
0972: + parameterName
0973: .substring(1); // NOI18N
0974: Tree parameterTree = parameter
0975: .getType();
0976: VariableTree oldParameter = make
0977: .Variable(
0978: make
0979: .Modifiers(Collections.EMPTY_SET),
0980: oldParameterName,
0981: parameterTree,
0982: persistentVariable);
0983: BlockTree newBlock = make
0984: .insertBlockStatement(
0985: block, 0,
0986: oldParameter);
0987:
0988: // changeSupport.firePropertyChange("<propertyName>", old<PropertyName>, <propertyName>);
0989: MemberSelectTree fireMethod = make
0990: .MemberSelect(
0991: make
0992: .Identifier("changeSupport"),
0993: "firePropertyChange"); // NOI18N
0994: List<ExpressionTree> fireArgs = new LinkedList<ExpressionTree>();
0995: fireArgs.add(make
0996: .Literal(propName));
0997: fireArgs
0998: .add(make
0999: .Identifier(oldParameterName));
1000: fireArgs
1001: .add(make
1002: .Identifier(parameterName));
1003: MethodInvocationTree notification = make
1004: .MethodInvocation(
1005: Collections.EMPTY_LIST,
1006: fireMethod,
1007: fireArgs);
1008: newBlock = make
1009: .addBlockStatement(
1010: newBlock,
1011: make
1012: .ExpressionStatement(notification));
1013: wc.rewrite(block, newBlock);
1014: }
1015: }
1016: }
1017: wc.rewrite(clazz, modifiedClass);
1018: }
1019:
1020: public void cancel() {
1021: }
1022:
1023: }).commit();
1024: if (alreadyUpdated[0])
1025: return;
1026: source.runModificationTask(
1027: new CancellableTask<WorkingCopy>() {
1028:
1029: public void run(WorkingCopy wc)
1030: throws Exception {
1031: wc.toPhase(JavaSource.Phase.RESOLVED);
1032: CompilationUnitTree cu = wc
1033: .getCompilationUnit();
1034: ClassTree clazz = null;
1035: for (Tree typeDecl : cu.getTypeDecls()) {
1036: if (Tree.Kind.CLASS == typeDecl
1037: .getKind()) {
1038: ClassTree candidate = (ClassTree) typeDecl;
1039: if (candidate
1040: .getModifiers()
1041: .getFlags()
1042: .contains(
1043: javax.lang.model.element.Modifier.PUBLIC)) {
1044: clazz = candidate;
1045: break;
1046: }
1047: }
1048: }
1049: TreeMaker make = wc.getTreeMaker();
1050:
1051: // addPropertyChange method
1052: ModifiersTree parMods = make.Modifiers(
1053: Collections.EMPTY_SET,
1054: Collections.EMPTY_LIST);
1055: TypeElement changeListenerElement = wc
1056: .getElements()
1057: .getTypeElement(
1058: "java.beans.PropertyChangeListener"); // NOI18N
1059: VariableTree par = make
1060: .Variable(
1061: parMods,
1062: "listener",
1063: make
1064: .QualIdent(changeListenerElement),
1065: null); // NOI18N
1066: TypeElement changeSupportElement = wc
1067: .getElements()
1068: .getTypeElement(
1069: "java.beans.PropertyChangeSupport"); // NOI18N
1070: VariableTree changeSupport = make
1071: .Variable(
1072: parMods,
1073: "changeSupport",
1074: make
1075: .QualIdent(changeSupportElement),
1076: null); // NOI18N
1077: MemberSelectTree addCall = make
1078: .MemberSelect(make
1079: .Identifier(changeSupport
1080: .getName()),
1081: "addPropertyChangeListener"); // NOI18N
1082: MethodInvocationTree addInvocation = make
1083: .MethodInvocation(
1084: Collections.EMPTY_LIST,
1085: addCall,
1086: Collections
1087: .singletonList(make
1088: .Identifier(par
1089: .getName())));
1090: MethodTree addMethod = make
1091: .Method(
1092: make
1093: .Modifiers(
1094: Modifier.PUBLIC,
1095: Collections.EMPTY_LIST),
1096: "addPropertyChangeListener", // NOI18N
1097: make
1098: .PrimitiveType(TypeKind.VOID),
1099: Collections.EMPTY_LIST,
1100: Collections
1101: .singletonList(par),
1102: Collections.EMPTY_LIST,
1103: make
1104: .Block(
1105: Collections
1106: .singletonList(make
1107: .ExpressionStatement(addInvocation)),
1108: false),
1109: null);
1110: ClassTree modifiedClass = make
1111: .addClassMember(clazz, addMethod);
1112: wc.rewrite(clazz, modifiedClass);
1113: }
1114:
1115: public void cancel() {
1116: }
1117:
1118: }).commit();
1119: source.runModificationTask(
1120: new CancellableTask<WorkingCopy>() {
1121:
1122: public void run(WorkingCopy wc)
1123: throws Exception {
1124: wc.toPhase(JavaSource.Phase.RESOLVED);
1125: CompilationUnitTree cu = wc
1126: .getCompilationUnit();
1127: ClassTree clazz = null;
1128: for (Tree typeDecl : cu.getTypeDecls()) {
1129: if (Tree.Kind.CLASS == typeDecl
1130: .getKind()) {
1131: ClassTree candidate = (ClassTree) typeDecl;
1132: if (candidate
1133: .getModifiers()
1134: .getFlags()
1135: .contains(
1136: javax.lang.model.element.Modifier.PUBLIC)) {
1137: clazz = candidate;
1138: break;
1139: }
1140: }
1141: }
1142: TreeMaker make = wc.getTreeMaker();
1143:
1144: // removePropertyChange method
1145: ModifiersTree parMods = make.Modifiers(
1146: Collections.EMPTY_SET,
1147: Collections.EMPTY_LIST);
1148: TypeElement changeListenerElement = wc
1149: .getElements()
1150: .getTypeElement(
1151: "java.beans.PropertyChangeListener"); // NOI18N
1152: VariableTree par = make
1153: .Variable(
1154: parMods,
1155: "listener",
1156: make
1157: .QualIdent(changeListenerElement),
1158: null); // NOI18N
1159: TypeElement changeSupportElement = wc
1160: .getElements()
1161: .getTypeElement(
1162: "java.beans.PropertyChangeSupport"); // NOI18N
1163: VariableTree changeSupport = make
1164: .Variable(
1165: parMods,
1166: "changeSupport",
1167: make
1168: .QualIdent(changeSupportElement),
1169: null); // NOI18N
1170: MemberSelectTree removeCall = make
1171: .MemberSelect(make
1172: .Identifier(changeSupport
1173: .getName()),
1174: "removePropertyChangeListener"); // NOI18N
1175: MethodInvocationTree removeInvocation = make
1176: .MethodInvocation(
1177: Collections.EMPTY_LIST,
1178: removeCall,
1179: Collections
1180: .singletonList(make
1181: .Identifier(par
1182: .getName())));
1183: MethodTree removeMethod = make
1184: .Method(
1185: make
1186: .Modifiers(
1187: Modifier.PUBLIC,
1188: Collections.EMPTY_LIST),
1189: "removePropertyChangeListener", // NOI18N
1190: make
1191: .PrimitiveType(TypeKind.VOID),
1192: Collections.EMPTY_LIST,
1193: Collections
1194: .singletonList(par),
1195: Collections.EMPTY_LIST,
1196: make
1197: .Block(
1198: Collections
1199: .singletonList(make
1200: .ExpressionStatement(removeInvocation)),
1201: false),
1202: null);
1203: ClassTree modifiedClass = make
1204: .addClassMember(clazz, removeMethod);
1205: wc.rewrite(clazz, modifiedClass);
1206: }
1207:
1208: public void cancel() {
1209: }
1210:
1211: }).commit();
1212: } catch (IOException ioex) {
1213: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
1214: ioex.getMessage(), ioex);
1215: }
1216: }
1217:
1218: public static List<String> typesOfProperties(
1219: FileObject fileInProject, String entityClass,
1220: final List<String> propertyNames) {
1221: ClassPath cp = ClassPath.getClassPath(fileInProject,
1222: ClassPath.SOURCE);
1223: String resourceName = entityClass.replace('.', '/') + ".java"; // NOI18N
1224: FileObject entity = cp.findResource(resourceName);
1225: final List<String> types = new LinkedList<String>();
1226: if (entity == null)
1227: return types;
1228: JavaSource source = JavaSource.forFileObject(entity);
1229: try {
1230: source.runUserActionTask(
1231: new CancellableTask<CompilationController>() {
1232: public void run(CompilationController cc)
1233: throws Exception {
1234: cc.toPhase(JavaSource.Phase.RESOLVED);
1235: CompilationUnitTree cu = cc
1236: .getCompilationUnit();
1237: ClassTree clazz = null;
1238: for (Tree typeDecl : cu.getTypeDecls()) {
1239: if (Tree.Kind.CLASS == typeDecl
1240: .getKind()) {
1241: ClassTree candidate = (ClassTree) typeDecl;
1242: if (candidate
1243: .getModifiers()
1244: .getFlags()
1245: .contains(
1246: javax.lang.model.element.Modifier.PUBLIC)) {
1247: clazz = candidate;
1248: break;
1249: }
1250: }
1251: }
1252: Map<String, String> variables = new HashMap<String, String>();
1253: Map<String, String> methods = new HashMap<String, String>();
1254: Element classElement = cc.getTrees()
1255: .getElement(
1256: cc.getTrees().getPath(cu,
1257: clazz));
1258: for (VariableElement variable : ElementFilter
1259: .fieldsIn(classElement
1260: .getEnclosedElements())) {
1261: String name = variable.getSimpleName()
1262: .toString();
1263: String type = variable.asType()
1264: .toString();
1265: variables.put(name, type);
1266: }
1267: for (ExecutableElement method : ElementFilter
1268: .methodsIn(classElement
1269: .getEnclosedElements())) {
1270: String type = method.getReturnType()
1271: .toString();
1272: String name = method.getSimpleName()
1273: .toString();
1274: if (name.startsWith("get")) { // NOI18N
1275: name = name.substring(3);
1276: } else if (name.startsWith("is")
1277: && type.equals("boolean")) { // NOI18N
1278: name = name.substring(2);
1279: } else {
1280: name = null;
1281: }
1282: if ((name != null)
1283: && (name.length() > 0)) {
1284: if ((name.length() == 1)
1285: || Character
1286: .isLowerCase(name
1287: .charAt(1))) {
1288: name = Character
1289: .toLowerCase(name
1290: .charAt(0))
1291: + name.substring(1);
1292: }
1293: methods.put(name, type);
1294: }
1295: }
1296: for (String name : propertyNames) {
1297: String type = methods.get(name);
1298: if (type == null) {
1299: type = variables.get(name);
1300: }
1301: if (type != null) {
1302: type = FormUtils.autobox(type);
1303: if (type.startsWith("java.lang.")) { // NOI18N
1304: type = type.substring(10);
1305: }
1306: type += ".class"; // NOI18N
1307: }
1308: types.add(type);
1309: }
1310: }
1311:
1312: public void cancel() {
1313: }
1314: }, true);
1315: } catch (IOException ioex) {
1316: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
1317: ioex.getMessage(), ioex);
1318: }
1319: return types;
1320: }
1321:
1322: /**
1323: * Determines properties of the entity that will be displayed as table columns.
1324: *
1325: * @param mappings information about entity mappings.
1326: * @param entityName of the entity.
1327: * @return list of property names.
1328: * @throws IOException when something goes wrong.
1329: */
1330: public static List<String> propertiesForColumns(
1331: MetadataModel<EntityMappingsMetadata> mappings,
1332: final String entityName, final List<String> columns)
1333: throws IOException {
1334: List<String> properties = Collections.emptyList();
1335: try {
1336: properties = mappings
1337: .runReadActionWhenReady(
1338: new MetadataModelAction<EntityMappingsMetadata, List<String>>() {
1339: public List<String> run(
1340: EntityMappingsMetadata metadata) {
1341: Entity[] entities = metadata
1342: .getRoot().getEntity();
1343: Entity entity = null;
1344: for (int i = 0; i < entities.length; i++) {
1345: if (entityName
1346: .equals(entities[i]
1347: .getName())) {
1348: entity = entities[i];
1349: break;
1350: }
1351: }
1352: if (entity == null) {
1353: return Collections.emptyList();
1354: }
1355: boolean all = (columns == null);
1356: List<String> props = new LinkedList<String>();
1357: Map<String, String> columnToProperty = all ? null
1358: : new HashMap<String, String>();
1359: Attributes attrs = entity
1360: .getAttributes();
1361: for (Id id : attrs.getId()) {
1362: String propName = J2EEUtils
1363: .fieldToProperty(id
1364: .getName());
1365: if (all) {
1366: props.add(propName);
1367: } else {
1368: String columnName = id
1369: .getColumn()
1370: .getName();
1371: columnName = unquote(columnName);
1372: columnToProperty.put(
1373: columnName,
1374: propName);
1375: }
1376: }
1377: for (Basic basic : attrs.getBasic()) {
1378: String propName = J2EEUtils
1379: .fieldToProperty(basic
1380: .getName());
1381: if ("<error>".equals(propName)) { // NOI18N
1382: continue;
1383: }
1384: if (all) {
1385: props.add(propName);
1386: } else {
1387: String columnName = basic
1388: .getColumn()
1389: .getName();
1390: columnName = unquote(columnName);
1391: columnToProperty.put(
1392: columnName,
1393: propName);
1394: }
1395: }
1396: for (ManyToOne manyToOne : attrs
1397: .getManyToOne()) {
1398: String propName = J2EEUtils
1399: .fieldToProperty(manyToOne
1400: .getName());
1401: if ("<error>".equals(propName)) { // NOI18N
1402: continue;
1403: }
1404: if (all) {
1405: props.add(propName);
1406: } else {
1407: String columnName = manyToOne
1408: .getJoinColumn(0)
1409: .getName();
1410: columnName = unquote(columnName);
1411: columnToProperty.put(
1412: columnName,
1413: propName);
1414: }
1415: }
1416: if (!all) {
1417: for (String column : columns) {
1418: String propName = columnToProperty
1419: .get(column);
1420: if (propName == null) {
1421: System.err
1422: .println("WARNING: Cannot find property for column "
1423: + column); // NOI18N
1424: } else {
1425: props.add(propName);
1426: }
1427: }
1428: }
1429: return props;
1430: }
1431: }).get();
1432: } catch (InterruptedException iex) {
1433: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
1434: iex.getMessage(), iex);
1435: } catch (ExecutionException eex) {
1436: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
1437: eex.getMessage(), eex);
1438: }
1439: return properties;
1440: }
1441:
1442: /**
1443: * Adds schema parameter into Table annotation if it is not present already.
1444: *
1445: * @param entity entity to update.
1446: * @param schema name of the schema.
1447: */
1448: private static void addSchemaParameter(FileObject entity,
1449: final String schema) {
1450: if (schema == null)
1451: return;
1452: JavaSource source = JavaSource.forFileObject(entity);
1453: try {
1454: source.runModificationTask(
1455: new CancellableTask<WorkingCopy>() {
1456: public void run(WorkingCopy wc)
1457: throws Exception {
1458: wc.toPhase(JavaSource.Phase.RESOLVED);
1459: CompilationUnitTree cu = wc
1460: .getCompilationUnit();
1461: ClassTree clazz = null;
1462: for (Tree typeDecl : cu.getTypeDecls()) {
1463: if (Tree.Kind.CLASS == typeDecl
1464: .getKind()) {
1465: ClassTree candidate = (ClassTree) typeDecl;
1466: if (candidate
1467: .getModifiers()
1468: .getFlags()
1469: .contains(
1470: javax.lang.model.element.Modifier.PUBLIC)) {
1471: clazz = candidate;
1472: break;
1473: }
1474: }
1475: }
1476: if (clazz == null)
1477: return;
1478: AnnotationTree tableAnn = null;
1479: for (AnnotationTree annotation : clazz
1480: .getModifiers().getAnnotations()) {
1481: Tree annotationType = annotation
1482: .getAnnotationType();
1483: Element classElement = wc
1484: .getTrees()
1485: .getElement(
1486: wc.getTrees().getPath(
1487: cu,
1488: annotationType));
1489: if ((classElement != null)
1490: && ("javax.persistence.Table"
1491: .equals(classElement
1492: .toString()))) { // NOI18N
1493: tableAnn = annotation;
1494: break;
1495: }
1496: }
1497: if (tableAnn == null)
1498: return;
1499: ExpressionTree schemaTree = null;
1500: for (ExpressionTree argument : tableAnn
1501: .getArguments()) {
1502: if (argument.getKind() == Tree.Kind.ASSIGNMENT) {
1503: AssignmentTree assignment = (AssignmentTree) argument;
1504: if ("schema".equals(assignment
1505: .getVariable().toString())) { // NOI18N
1506: schemaTree = assignment;
1507: break;
1508: }
1509: }
1510: }
1511: if (schemaTree == null) {
1512: TreeMaker make = wc.getTreeMaker();
1513: IdentifierTree nameTree = make
1514: .Identifier("schema"); // NOI18N
1515: LiteralTree valueTree = make
1516: .Literal(schema);
1517: schemaTree = make.Assignment(nameTree,
1518: valueTree);
1519: AnnotationTree newTableAnn = make
1520: .addAnnotationAttrValue(
1521: tableAnn, schemaTree);
1522: wc.rewrite(tableAnn, newTableAnn);
1523: }
1524: }
1525:
1526: public void cancel() {
1527: }
1528: }).commit();
1529: } catch (IOException ex) {
1530: Logger.getLogger(J2EEUtils.class.getName()).log(Level.INFO,
1531: ex.getMessage(), ex);
1532: }
1533: }
1534:
1535: private static SourceGroup[] getJavaSourceGroups(Project project) {
1536: Parameters.notNull("project", project); //NOI18N
1537: SourceGroup[] sourceGroups = ProjectUtils
1538: .getSources(project)
1539: .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
1540: List<SourceGroup> result = new ArrayList<SourceGroup>();
1541: for (SourceGroup sourceGroup : sourceGroups) {
1542: if (UnitTestForSourceQuery.findUnitTests(sourceGroup
1543: .getRootFolder()).length > 0) {
1544: result.add(sourceGroup);
1545: }
1546: }
1547: return result.toArray(new SourceGroup[result.size()]);
1548: }
1549: }
|