| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.motorola.studio.android.codeutils.db.utils; |
| |
| import static com.motorola.studio.android.common.log.StudioLogger.info; |
| |
| import java.io.BufferedWriter; |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.datatools.connectivity.ConnectionProfileException; |
| import org.eclipse.datatools.connectivity.IConnection; |
| import org.eclipse.datatools.connectivity.IConnectionProfile; |
| import org.eclipse.datatools.connectivity.IManagedConnection; |
| import org.eclipse.datatools.connectivity.ProfileManager; |
| import org.eclipse.datatools.connectivity.drivers.DriverManager; |
| import org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo; |
| import org.eclipse.datatools.connectivity.sqm.core.connection.DatabaseConnectionRegistry; |
| import org.eclipse.datatools.connectivity.sqm.internal.core.connection.ConnectionInfoImpl; |
| import org.eclipse.datatools.modelbase.sql.schema.Catalog; |
| import org.eclipse.datatools.modelbase.sql.schema.Database; |
| import org.eclipse.datatools.modelbase.sql.schema.Schema; |
| import org.eclipse.datatools.modelbase.sql.tables.Table; |
| import org.eclipse.datatools.sqltools.data.internal.ui.editor.TableDataEditor; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.formatter.CodeFormatter; |
| import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.text.edits.TextEdit; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbenchPage; |
| |
| import com.motorola.studio.android.codeutils.CodeUtilsActivator; |
| import com.motorola.studio.android.codeutils.db.actions.ContentProviderGeneratorByTable; |
| import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS; |
| import com.motorola.studio.android.common.CommonPlugin; |
| import com.motorola.studio.android.common.exception.AndroidException; |
| import com.motorola.studio.android.common.log.StudioLogger; |
| import com.motorola.studio.android.common.log.UsageDataConstants; |
| import com.motorola.studio.android.common.utilities.EclipseUtils; |
| import com.motorola.studio.android.common.utilities.FileUtil; |
| import com.motorola.studio.android.db.deployment.DatabaseDeployer; |
| import com.motorola.studio.android.manifest.AndroidProjectManifestFile; |
| import com.motorola.studio.android.model.manifest.AndroidManifestFile; |
| import com.motorola.studio.android.model.manifest.dom.ManifestNode; |
| |
| @SuppressWarnings("restriction") |
| public class DatabaseUtils |
| { |
| |
| // Candidate folders that may contain .db resources |
| public static final String ASSESTS_FOLDER = "assets"; //$NON-NLS-1$ |
| |
| public static final String RAW_FOLDER = "raw"; //$NON-NLS-1$ |
| |
| public static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:"; |
| |
| public static final String URL_PROPERTY = "org.eclipse.datatools.connectivity.db.URL"; //$NON-NLS-1$ |
| |
| public static final String DBNAME_PROPERTY = |
| "org.eclipse.datatools.connectivity.db.databaseName"; //$NON-NLS-1$ |
| |
| // private static final String DEVICENAME_PROPERTY = |
| // "org.eclipse.datatools.connectivity.db.deviceName"; //$NON-NLS-1$ |
| |
| // private static final String SERIAL_PROPERTY = "com.motorola.studio.db.serialProperty"; //$NON-NLS-1$ |
| |
| // private static final String APPNAME_PROPERTY = "com.motorola.studio.db.appNameProperty"; //$NON-NLS-1$ |
| |
| public static final String REMOTEPATH_PROPERTY = "com.motorola.studio.db.remotePathProperty"; //$NON-NLS-1$ |
| |
| public static final String LOCALPATH_PROPERTY = "com.motorola.studio.db.localPathProperty"; //$NON-NLS-1$ |
| |
| public static final String TYPE_PROPERTY = "org.eclipse.datatools.connectivity.db.TYPE"; |
| |
| public static final String PROVIDER_ID = |
| "org.eclipse.datatools.enablement.sqlite.connectionProfile"; //$NON-NLS-1$ |
| |
| private static final String TEMPLATE_ID = |
| "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"; //$NON-NLS-1 |
| |
| public static final String DB_FOLDER = "assets"; |
| |
| public static Set<IConnectionProfile> profilesBeingDisconnected = |
| new HashSet<IConnectionProfile>(); |
| |
| /** |
| * Check if a file is a valid SQLite Database by analyzing the first 16 bytes block, |
| * since every SQLite database begins with the byte sequence: |
| * |
| * 0x53 0x51 0x4c 0x69 0x74 0x65 0x20 0x66 0x6f 0x72 0x6d 0x61 0x74 0x20 0x33 0x00 |
| * |
| * Source: http://www.sqlite.org/fileformat.html#database_header |
| * |
| * @param databaseFile The file to be checked. |
| * @return True if {@code databaseFile} is a valid SQLite database file. Otherwise, returns false. |
| * @throws IOException Thrown if there were errors reading the file. |
| */ |
| public static boolean isValidSQLiteDatabase(File databaseFile) throws IOException |
| { |
| boolean result = true; |
| |
| final int BYTE_ARRAY_SIZE = 16; |
| |
| byte[] headerByteArray = new byte[16]; |
| headerByteArray[0] = 0x53; |
| headerByteArray[1] = 0x51; |
| headerByteArray[2] = 0x4c; |
| headerByteArray[3] = 0x69; |
| headerByteArray[4] = 0x74; |
| headerByteArray[5] = 0x65; |
| headerByteArray[6] = 0x20; |
| headerByteArray[7] = 0x66; |
| headerByteArray[8] = 0x6f; |
| headerByteArray[9] = 0x72; |
| headerByteArray[10] = 0x6d; |
| headerByteArray[11] = 0x61; |
| headerByteArray[12] = 0x74; |
| headerByteArray[13] = 0x20; |
| headerByteArray[14] = 0x33; |
| headerByteArray[15] = 0x00; |
| |
| byte[] fileByteArray = new byte[BYTE_ARRAY_SIZE]; |
| FileInputStream fis = null; |
| |
| try |
| { |
| fis = new FileInputStream(databaseFile); |
| fis.read(fileByteArray); |
| } |
| finally |
| { |
| if (fis != null) |
| { |
| try |
| { |
| fis.close(); |
| } |
| catch (IOException e) |
| { |
| StudioLogger |
| .error("Could not close stream while checking if a file is a sqlite valid database" |
| + e.getMessage()); |
| } |
| } |
| } |
| |
| ByteArrayInputStream bais = null; |
| try |
| { |
| bais = new ByteArrayInputStream(fileByteArray); |
| for (int aux = 0; aux < BYTE_ARRAY_SIZE; aux++) |
| { |
| int myByte = bais.read(); |
| if (myByte != headerByteArray[aux]) |
| { |
| result = false; |
| } |
| } |
| } |
| finally |
| { |
| if (bais != null) |
| { |
| try |
| { |
| bais.close(); |
| } |
| catch (IOException e) |
| { |
| StudioLogger |
| .error("Could not close stream while checking if a file is a sqlite valid database" |
| + e.getMessage()); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Retrieve a collections of .db resources declared inside an IProject in the workspace. |
| * @param project The project to be considered. |
| */ |
| public static Set<IFile> getDbFilesFromProject(IProject project) |
| { |
| // Result containing the .db resources |
| HashSet<IFile> dbCollection = new HashSet<IFile>(); |
| |
| // List of candidate folder to be inspected |
| HashSet<IFolder> folderCollection = new HashSet<IFolder>(); |
| |
| // First, retrieve and check if the folders likely to contain the .db files exist |
| folderCollection.add(project.getFolder(ASSESTS_FOLDER)); |
| folderCollection.add(project.getFolder(RAW_FOLDER)); |
| |
| // Iterate through the folders and retrieve the .db IFiles |
| for (IFolder folder : folderCollection) |
| { |
| if (folder.exists()) |
| { |
| // Get a list of files in the folder and try to find the .db files |
| try |
| { |
| for (IResource resource : folder.members()) |
| { |
| // Check if it's a file |
| if (resource.getType() == IResource.FILE) |
| { |
| IFile file = (IFile) resource; |
| |
| // Check if the file is a valid database |
| try |
| { |
| if (file.exists() |
| & isValidSQLiteDatabase(file.getLocation().toFile())) |
| { |
| dbCollection.add(file); |
| } |
| } |
| catch (IOException e) |
| { |
| StudioLogger |
| .warn(DatabaseUtils.class, |
| "It was not possible verify if the file is a valid SQLite database", |
| e); |
| } |
| } |
| } |
| } |
| catch (CoreException e) |
| { |
| // Log error |
| StudioLogger.error(DatabaseUtils.class, |
| "An error ocurred while looking for .db files.", e); //$NON-NLS-1$ |
| } |
| } |
| } |
| return dbCollection; |
| |
| } |
| |
| /** |
| * @param database The database that will have its table retrieved. |
| * @return All tables in the {@code database}. |
| * */ |
| @SuppressWarnings( |
| { |
| "unchecked", "rawtypes" |
| }) |
| public static Set<Table> getTables(Database database) |
| { |
| HashSet<Table> tableSet = new HashSet<Table>(); |
| ListIterator<Catalog> catalogIter = database.getCatalogs().listIterator(); |
| while (catalogIter.hasNext()) |
| { |
| Catalog catalog = catalogIter.next(); |
| EList schemas = catalog.getSchemas(); |
| if ((schemas != null) && (schemas.size() > 0)) |
| { |
| ListIterator<Schema> schemasIter = schemas.listIterator(); |
| while (schemasIter.hasNext()) |
| { |
| Schema schema = schemasIter.next(); |
| EList tables = schema.getTables(); |
| if ((tables != null) && (tables.size() > 0)) |
| { |
| ListIterator<Table> tablesIter = tables.listIterator(); |
| while (tablesIter.hasNext()) |
| { |
| tableSet.add(tablesIter.next()); |
| } |
| } |
| } |
| } |
| } |
| return tableSet; |
| } |
| |
| /** |
| * Creates Database management classes |
| * |
| * @param project Target Project |
| * @param databaseName Database name |
| * @param generateSQLOpenHelperClases <code>true</code> for generating SQL Open Helper classes |
| * @param generateContentProviderClasses <code>true</code> in case |
| * it is desired to create Content Provider classes, <code>false</code> otherwise |
| * @param openHelperPackageName Open Helper Package Name |
| * @param contentProvidersPackageName Content provider Package Name |
| * @param sqlOpenHelperClassName SQL Open Helper Class name |
| * @param overrideContentProviders <code>true</code> in order to override the Content Providers |
| * @param generateDAO <code>true</code> in case one wishes to create DAO classes, <code>false</code> |
| * otherwise |
| * @param monitor Monitor of the process |
| * |
| * @throws AndroidException Exception thrown when there are problems handling Android files |
| * @throws CoreException Exception thrown when there are problems handling Eclipse |
| * @throws IOException Exception thrown when there are I/O problems with the Database |
| * @throws ConnectionProfileException Exception thrown when there are problems handling the database connection |
| * @throws SQLException Exception thrown when there are errors dealing with SQL |
| */ |
| public static void createDatabaseManagementClasses(IProject project, String databaseName, |
| boolean generateSQLOpenHelperClases, boolean generateContentProviderClasses, |
| String openHelperPackageName, String contentProvidersPackageName, |
| String sqlOpenHelperClassName, boolean overrideContentProviders, boolean generateDAO, |
| IProgressMonitor monitor, boolean showSuccessDialog) throws IOException, CoreException, |
| AndroidException, ConnectionProfileException, SQLException |
| { |
| // get sub monitor |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 10); |
| |
| // begin |
| subMonitor.beginTask(null, 10); |
| |
| // create parameters and copy database deployer class to the android project |
| Map<String, String> dbParameters = new HashMap<String, String>(); |
| |
| AndroidManifestFile androidManifestFile = |
| AndroidProjectManifestFile.getFromProject(project); |
| |
| ManifestNode manifestNode = |
| androidManifestFile != null ? androidManifestFile.getManifestNode() : null; |
| |
| String appNamespace = manifestNode.getPackageName().toLowerCase(); |
| String packageName = ""; //$NON-NLS-1$ |
| if (openHelperPackageName != null) |
| { |
| //user-defined package for deployer |
| packageName = openHelperPackageName; |
| } |
| else |
| { |
| //use default package for deployer |
| packageName = appNamespace + ".deployer"; //$NON-NLS-1$ |
| } |
| |
| dbParameters.put(DatabaseDeployer.DATABASE_NAME, databaseName); |
| dbParameters.put(DatabaseDeployer.APPLICATION_DATABASE_NAMESPACE, appNamespace); |
| dbParameters.put(DatabaseDeployer.ANDROID_PROJECT_PACKAGE_NAME, packageName); |
| dbParameters.put(DatabaseDeployer.DATABASE_HELPER_CLASS_NAME, sqlOpenHelperClassName); |
| |
| subMonitor.worked(1); |
| |
| if (generateSQLOpenHelperClases) |
| { |
| DatabaseDeployer.copyDataBaseDeployerClassToProject(project, dbParameters, |
| subMonitor.newChild(2)); |
| StudioLogger.info("Finished creating Deployer classes"); //$NON-NLS-1$ |
| |
| // Creates UDC log reporting that an OpenHelper class was created |
| StudioLogger.collectUsageData(UsageDataConstants.WHAT_OPENHELPER, //$NON-NLS-1$ |
| UsageDataConstants.KIND_OPENHELPER, "generated SQLOpenHelper class", //$NON-NLS-1$ |
| CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle() |
| .getVersion().toString()); |
| |
| } |
| |
| // variables for connecting to the database |
| IConnectionProfile profile = null; |
| boolean isConnectionOK = false; |
| boolean isConnectionStartedHere = false; |
| IStatus status = null; |
| try |
| { |
| // get local database profile |
| profile = DatabaseUtils.getLocalDbProfile(project.getName(), databaseName); |
| // continue in case there is a profile |
| if (profile != null) |
| { |
| // assert driver |
| DatabaseUtils.assertDriverExistsAtModel(); |
| // if the connection is established, set the flag of the connection to true |
| if (profile.getConnectionState() == IConnectionProfile.CONNECTED_STATE) |
| { |
| // set the flag |
| isConnectionOK = true; |
| } |
| // the connection is not established, therefore connect |
| else if (profile.getConnectionState() == IConnectionProfile.DISCONNECTED_STATE) |
| { |
| // execute connection and get the status |
| status = profile.connectWithoutJob(); |
| // state that the connection was established here, thus the disconnection is required |
| isConnectionStartedHere = true; |
| // set the connection flag to OK |
| if ((status != null) && (status.getCode() == IStatus.OK)) |
| { |
| isConnectionOK = true; |
| } |
| } |
| |
| subMonitor.worked(4); |
| |
| // proceed in case the connection is established and OK |
| if (isConnectionOK) |
| { |
| String appendMsg = CodeUtilsNLS.DATABASE_DEPLOY_SUCCESS_MESSAGE; |
| boolean createMetadata = true; |
| String query = "SELECT * FROM \"android_metadata\""; //$NON-NLS-1$ |
| ResultSet rs = null; |
| |
| try |
| { |
| rs = DatabaseUtils.executeSqliteQuery(profile, query); |
| |
| if (rs != null) |
| { |
| if (rs.next()) |
| { |
| createMetadata = false; |
| } |
| } |
| } |
| finally |
| { |
| if (rs != null) |
| { |
| rs.close(); |
| } |
| } |
| if (createMetadata) |
| { |
| // create the tables and insert data |
| DatabaseUtils |
| .executeSqliteStatement(profile, |
| "CREATE TABLE IF NOT EXISTS \"android_metadata\" (\"locale\" TEXT DEFAULT 'en_US');"); //$NON-NLS-1$ |
| DatabaseUtils |
| .executeSqliteStatement(profile, |
| "insert into DEFAULT.ANDROID_METADATA (\"locale\") values('en_US');"); //$NON-NLS-1$ |
| } |
| |
| subMonitor.worked(2); |
| |
| if (generateContentProviderClasses) |
| { |
| Database database = DatabaseUtils.getDatabaseForProfile(profile); |
| DatabaseUtils.createPersistenceClassesForDatabase(project, database, false, |
| false, contentProvidersPackageName, packageName, |
| sqlOpenHelperClassName, overrideContentProviders, generateDAO); |
| |
| subMonitor.worked(2); |
| } |
| |
| subMonitor.worked(1); |
| |
| if (showSuccessDialog) |
| { |
| // show success message |
| EclipseUtils.showInformationDialog( |
| CodeUtilsNLS.DATABASE_DEPLOY_SUCCESS_MESSAGE_TITLE, appendMsg); |
| } |
| } |
| else |
| { |
| // retrieve status error message |
| String errorMessage = ""; //$NON-NLS-1$ |
| Throwable exception = null; |
| if (status != null) |
| { |
| exception = status.getException(); |
| if (exception != null) |
| { |
| // get error message |
| errorMessage = exception.getLocalizedMessage(); |
| } |
| } |
| // print and log database connection error message |
| StudioLogger.error(DatabaseUtils.class, |
| CodeUtilsNLS.DATABASE_DEPLOY_ERROR_CONNECTING_DATABASE, exception); |
| EclipseUtils.showErrorDialog( |
| CodeUtilsNLS.DATABASE_DEPLOY_CREATING_ANDROID_METADATA_TABLE, |
| CodeUtilsNLS.DATABASE_DEPLOY_ERROR_CONNECTING_DATABASE |
| + ((errorMessage != null) && (errorMessage.length() > 0) |
| ? "\r\n" + errorMessage : ""), status); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| finally |
| { |
| // in case the connection was established, here, close it |
| if (isConnectionStartedHere && (profile != null // there is a profile |
| ) && (profile.getConnectionState() == IConnectionProfile.CONNECTED_STATE // the connection is up and running |
| ) && DatabaseUtils.prepareConnectionToClose(profile, false)) // prepare the connection to close |
| { |
| // close the connection |
| profile.disconnect(null); |
| } |
| } |
| } |
| |
| /** |
| * @return The connection profile to the given database. Create the connection profile if it does not exist yet. |
| * */ |
| public static IConnectionProfile getLocalDbProfile(String projectName, String databaseName) |
| throws ConnectionProfileException |
| { |
| ProfileManager pm = ProfileManager.getInstance(); |
| String profileName = projectName + "." + databaseName; //$NON-NLS-1$ |
| IConnectionProfile profile = pm.getProfileByName(profileName); |
| if (profile == null) |
| { |
| String driverPath = CommonPlugin.getDefault().getDriverPath(); |
| Properties prop = DatabaseUtils.getBaseConnProperties(driverPath, databaseName); |
| |
| String providerId = DatabaseUtils.PROVIDER_ID; |
| profile = pm.createProfile(profileName, "", providerId, prop); //$NON-NLS-1$ |
| |
| String dbPath = |
| ResourcesPlugin.getWorkspace().getRoot().getProject(projectName) |
| .getFolder(DB_FOLDER).getFile(databaseName).getLocation().toString(); |
| prop.put(DatabaseUtils.URL_PROPERTY, DatabaseUtils.JDBC_SQLITE_PREFIX + dbPath); //$NON-NLS-1$ |
| prop.put(DatabaseUtils.LOCALPATH_PROPERTY, dbPath); |
| profile.setBaseProperties(prop); |
| } |
| return profile; |
| } |
| |
| /** |
| * Return the following set of properties to be used in a connection profile: |
| * <ul> |
| * <li>org.eclipse.datatools.connectivity.db.vendor = "SQLITE"</li> |
| * <li>org.eclipse.datatools.connectivity.db.password = ""</li> |
| * <li>org.eclipse.datatools.connectivity.driverDefinitionID = "DriverDefn.org.eclipse.datatools.enablement.sqlite.3_5_9.driver." + {@link CommonPlugin#JDBC_DRIVER_INSTANCE_NAME}</li> |
| * <li>org.eclipse.datatools.connectivity.drivers.defnType = "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"</li> |
| * <li>org.eclipse.datatools.connectivity.db.savePWD = "false"</li> |
| * <li>org.eclipse.datatools.connectivity.db.connectionProperties = ""</li> |
| * <li>org.eclipse.datatools.connectivity.db.version = "3.5.9"</li> |
| * <li>org.eclipse.datatools.connectivity.db.databaseName = {@code dbName}</li> |
| * <li>jarList = {@code driverPath}</li> |
| * <li>org.eclipse.datatools.connectivity.db.username = ""</li> |
| * <li>org.eclipse.datatools.connectivity.db.driverClass = "org.sqlite.JDBC"</li> |
| * </ul> |
| * */ |
| public static Properties getBaseConnProperties(String driverPath, String dbName) |
| { |
| Properties prop = new Properties(); |
| prop.put("org.eclipse.datatools.connectivity.db.vendor", "SQLITE"); //$NON-NLS-1$ //$NON-NLS-2$ |
| prop.put("org.eclipse.datatools.connectivity.db.password", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| prop.put("org.eclipse.datatools.connectivity.driverDefinitionID", //$NON-NLS-1$ |
| "DriverDefn.org.eclipse.datatools.enablement.sqlite.3_5_9.driver." //$NON-NLS-1$ |
| + CommonPlugin.JDBC_DRIVER_INSTANCE_NAME); |
| prop.put("org.eclipse.datatools.connectivity.drivers.defnType", //$NON-NLS-1$ |
| "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"); //$NON-NLS-1$ |
| prop.put("org.eclipse.datatools.connectivity.db.savePWD", "false"); //$NON-NLS-1$ //$NON-NLS-2$ |
| prop.put("org.eclipse.datatools.connectivity.db.connectionProperties", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| prop.put("org.eclipse.datatools.connectivity.db.version", "3.5.9"); //$NON-NLS-1$ //$NON-NLS-2$ |
| prop.put(DatabaseUtils.DBNAME_PROPERTY, dbName); |
| prop.put("jarList", driverPath); //$NON-NLS-1$ |
| prop.put("org.eclipse.datatools.connectivity.db.username", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| prop.put("org.eclipse.datatools.connectivity.db.driverClass", "org.sqlite.JDBC"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| return prop; |
| } |
| |
| /** |
| * Checks if a compatible JDBC driver is registered. If not, registers one. |
| */ |
| public static void assertDriverExistsAtModel() |
| { |
| DriverManager driverMan = DriverManager.getInstance(); |
| String allDrivers = driverMan.getFullJarList(); |
| String driverPath = CommonPlugin.getDefault().getDriverPath(); |
| if ((allDrivers == null) || (!allDrivers.contains(driverPath))) |
| { |
| String templateId = DatabaseUtils.TEMPLATE_ID; |
| driverMan.createNewDriverInstance(templateId, CommonPlugin.JDBC_DRIVER_INSTANCE_NAME, |
| driverPath); |
| info("Created a MOTODEV Studio JDBC driver instance at Data Tools."); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Executes Sqlite query and return a {@link ResultSet}. |
| * @param profile The profile representing the database to be queried. |
| * @param query SQL statement (select). |
| * @return SQL Results (columns and values). |
| */ |
| public static ResultSet executeSqliteQuery(IConnectionProfile profile, String query) |
| { |
| ResultSet resultSet = null; |
| java.sql.Connection conn = DatabaseUtils.getJavaConnectionForProfile(profile); |
| if (conn != null) |
| { |
| try |
| { |
| java.sql.Statement stmt = conn.createStatement(); |
| resultSet = stmt.executeQuery(query); |
| } |
| catch (java.sql.SQLException sqle) |
| { |
| StudioLogger.error(DatabaseUtils.class, "Problems executing query", sqle); //$NON-NLS-1$ |
| } |
| } |
| return resultSet; |
| } |
| |
| /** |
| * @param profile A datatools connection profile. |
| * @return A java sql connection to make create, insert, delete, update calls to database. |
| */ |
| public static java.sql.Connection getJavaConnectionForProfile(IConnectionProfile profile) |
| { |
| IManagedConnection managedConnection = |
| (profile).getManagedConnection("java.sql.Connection"); //$NON-NLS-1$ |
| if (managedConnection != null) |
| { |
| return (java.sql.Connection) managedConnection.getConnection().getRawConnection(); |
| } |
| return null; |
| } |
| |
| /** |
| * Executes Sqlite statements that does not return items (create, update, delete). |
| * @param profile A datatools connection profile. |
| * @param query SQL statement (create, update, delete) |
| * @return Same as {@link Statement#executeUpdate(String)}. |
| */ |
| public static int executeSqliteStatement(IConnectionProfile profile, String query) |
| { |
| int count = 0; |
| java.sql.Connection conn = getJavaConnectionForProfile(profile); |
| if (conn != null) |
| { |
| try |
| { |
| java.sql.Statement stmt = conn.createStatement(); |
| count = stmt.executeUpdate(query); |
| } |
| catch (java.sql.SQLException sqle) |
| { |
| StudioLogger.error(DatabaseUtils.class, |
| CodeUtilsNLS.DATABASE_ERROR_EXECUTING_STATEMENT, sqle); |
| } |
| } |
| return count; |
| } |
| |
| /** |
| * Search for database to get model (tables and colums definitions). |
| * WARNING: check return after proceeding, because if the database is not connected, it will be null. |
| * @param profile A datatools connection profile. |
| * @return A datatools database abstraction. |
| */ |
| public static Database getDatabaseForProfile(IConnectionProfile profile) |
| { |
| IManagedConnection managedConnection = |
| (profile) |
| .getManagedConnection("org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo"); //$NON-NLS-1$ |
| if (managedConnection != null) |
| { |
| try |
| { |
| IConnection conn = managedConnection.getConnection(); |
| if (conn != null) |
| { |
| ConnectionInfo connectionInfo = (ConnectionInfo) conn.getRawConnection(); |
| if (connectionInfo != null) |
| { |
| return connectionInfo.getSharedDatabase(); |
| } |
| } |
| } |
| catch (Exception e) |
| { |
| StudioLogger.error(DatabaseUtils.class, "Problems executing query", e); //$NON-NLS-1$ |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Generates Persistence classes for the tables on db |
| * @param project |
| * @param database |
| * @param addCreateTableStatement add create table statement on helper |
| * @param addDropTableStatementOnUpdate add drop statement on helper |
| * @param persistencePackageName package where to place the persistence classes on project |
| * @param databaseOpenHelperPackageName Database Open Helper Package Name |
| * @param databaseOpenHelperClassName Database open Helper Class Name |
| * @param overrideContentProviders <code>true</code> in case one whishes to override the Content Providers |
| * in case they exist |
| * @param generateDAO false create Content Provider, true create DAO (DAO should NOT be used now) |
| * @throws IOException |
| * @throws CoreException |
| * @throws AndroidException |
| */ |
| @SuppressWarnings( |
| { |
| "unchecked", "rawtypes" |
| }) |
| public static void createPersistenceClassesForDatabase(IProject project, Database database, |
| boolean addCreateTableStatement, boolean addDropTableStatementOnUpdate, |
| String persistencePackageName, String databaseOpenHelperPackageName, |
| String databaseOpenHelperClassName, boolean overrideContentProviders, |
| boolean generateDAO) throws IOException, CoreException, AndroidException |
| { |
| ListIterator<Catalog> catalogIter = database.getCatalogs().listIterator(); |
| while (catalogIter.hasNext()) |
| { |
| Catalog catalog = catalogIter.next(); |
| EList schemas = catalog.getSchemas(); |
| if ((schemas != null) && (schemas.size() > 0)) |
| { |
| ListIterator<Schema> schemasIter = schemas.listIterator(); |
| while (schemasIter.hasNext()) |
| { |
| Schema schema = schemasIter.next(); |
| EList tables = schema.getTables(); |
| |
| //this list will be created to control the classes names. The name for each table will be put in CamelCase and |
| //all underscores "_" will be removed. If two name are equals, we will have to put a counter in the end o the name. |
| //This list will hold all the names that were created. |
| List<String> tableNameForClasses = new ArrayList<String>(); |
| if ((tables != null) && (tables.size() > 0)) |
| { |
| ListIterator<Table> tablesIter = tables.listIterator(); |
| while (tablesIter.hasNext()) |
| { |
| Table table = tablesIter.next(); |
| StudioLogger.info("Start creating persistence classes for table " //$NON-NLS-1$ |
| + table.getName()); |
| |
| //generate Content Provider |
| DatabaseUtils.generateContentProvider(project, table, database, |
| addCreateTableStatement, addDropTableStatementOnUpdate, |
| overrideContentProviders, persistencePackageName, |
| databaseOpenHelperPackageName, databaseOpenHelperClassName, |
| tableNameForClasses); |
| StudioLogger.collectUsageData("generateContentProviderClasses", //$NON-NLS-1$ |
| "database", UsageDataConstants.DESCRIPTION_DEFAULT, //$NON-NLS-1$ |
| CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault() |
| .getBundle().getVersion().toString()); |
| |
| StudioLogger.info("Finished creating persistence classes for table " //$NON-NLS-1$ |
| + table.getName()); |
| } |
| } |
| } |
| } |
| |
| } |
| } |
| |
| /** |
| * Generates Content Provider class for the table |
| * @param project |
| * @param table |
| * @param database |
| * @param addCreateTableStatement |
| * @param addDropTableStatementOnUpdate |
| * @param overrideContentProviders <code>true</code> in case one wishes to override the Content Providers |
| * @param persistencePackageName |
| * @param databaseOpenHelperPackageName Database Open Helper package name |
| * @param databaseOpenHelperClassName Database Open Helper class name |
| * @param beanName |
| * @throws IOException |
| * @throws CoreException |
| * @throws AndroidException |
| */ |
| public static void generateContentProvider(IProject project, Table table, Database database, |
| boolean addCreateTableStatement, boolean addDropTableStatementOnUpdate, |
| boolean overrideContentProviders, String persistencePackageName, |
| String databaseOpenHelperPackageName, String databaseOpenHelperClassName, |
| List<String> tableNameForClasses) throws IOException, CoreException, AndroidException |
| { |
| String dbName = database.getName(); |
| ContentProviderGeneratorByTable contentProviderGeneratorByTable = |
| new ContentProviderGeneratorByTable(table, dbName); |
| contentProviderGeneratorByTable.createContentProvider(project, addCreateTableStatement, |
| addDropTableStatementOnUpdate, overrideContentProviders, persistencePackageName, |
| databaseOpenHelperPackageName, databaseOpenHelperClassName, tableNameForClasses); |
| } |
| |
| /** |
| * Prepare a connection profile to close means closing all its opened editor. |
| * If the device related to the connection profile will be disconnected, set {@code willDiscDevice} to true. |
| * @return True if the connection profile can be safely closed. Otherwise, returns false. |
| * */ |
| public static boolean prepareConnectionToClose(IConnectionProfile profile, |
| boolean willDiscDevice) |
| { |
| final boolean[] success = new boolean[] |
| { |
| true |
| }; |
| |
| DatabaseUtils.profilesBeingDisconnected.add(profile); |
| Set<IEditorPart> editorsSet = DatabaseUtils.getEditorsForProfile(profile); |
| |
| for (final IEditorPart editor : editorsSet) |
| { |
| final IWorkbenchPage page = EclipseUtils.getPageForEditor(editor); |
| Display.getDefault().syncExec(new Runnable() |
| { |
| @Override |
| public void run() |
| { |
| page.bringToTop(editor); |
| if (!page.closeEditor(editor, true)) |
| { |
| success[0] = false; |
| } |
| } |
| }); |
| |
| if (!success[0]) |
| { |
| break; |
| } |
| } |
| |
| if (!willDiscDevice) |
| { |
| DatabaseUtils.profilesBeingDisconnected.remove(profile); |
| } |
| |
| return success[0]; |
| } |
| |
| /** |
| * Retrieves the open editors that is used to edit the given profile, if any. |
| * |
| * @param profile |
| * The profile that owns the requested editor. |
| * @return The open editors for the given profile, or <code>null</code> |
| * if there is no opened editor for this profile. |
| */ |
| public static Set<IEditorPart> getEditorsForProfile(IConnectionProfile profile) |
| { |
| Collection<IEditorPart> allEditors = EclipseUtils.getAllOpenedEditors(); |
| Set<IEditorPart> selectedEditors = new HashSet<IEditorPart>(); |
| |
| for (IEditorPart e : allEditors) |
| { |
| if (e instanceof TableDataEditor) |
| { |
| TableDataEditor tde = (TableDataEditor) e; |
| Table table = tde.getSqlTable(); |
| Catalog cat = table.getSchema().getCatalog(); |
| Database database = |
| cat != null ? cat.getDatabase() : table.getSchema().getDatabase(); |
| ConnectionInfo connInfo = |
| DatabaseConnectionRegistry.getConnectionForDatabase(database); |
| if (connInfo != null) |
| { |
| IConnectionProfile editorProfile = |
| ((ConnectionInfoImpl) connInfo).getConnectionProfile(); |
| if (editorProfile == profile) |
| { |
| selectedEditors.add(e); |
| } |
| } |
| } |
| } |
| |
| return selectedEditors; |
| } |
| |
| /** |
| * @param projectName The project which contains the database. |
| * @param dbNameWithExtension The complete database name, including its extension. |
| * @return A datatools abstraction for the database of the given project. |
| * */ |
| public static Database getDatabase(String projectName, String dbNameWithExtension) |
| throws ConnectionProfileException, IOException |
| { |
| // variables for connecting to the database |
| Database database = null; |
| IConnectionProfile profile = null; |
| boolean isConnectionOK = false; |
| IStatus status = null; |
| |
| // get local database profile |
| profile = getLocalDbProfile(projectName, dbNameWithExtension); |
| // continue in case there is a profile |
| if (profile != null) |
| { |
| // assert driver |
| assertDriverExistsAtModel(); |
| // if the connection is established, set the flag of the connection to true |
| if (profile.getConnectionState() == IConnectionProfile.CONNECTED_STATE) |
| { |
| // set the flag |
| isConnectionOK = true; |
| } |
| // the connection is not established, therefore connect |
| else if (profile.getConnectionState() == IConnectionProfile.DISCONNECTED_STATE) |
| { |
| // execute connection and get the status |
| status = profile.connectWithoutJob(); |
| // set the connection flag to OK |
| if ((status != null) && (status.getCode() == IStatus.OK)) |
| { |
| isConnectionOK = true; |
| } |
| } |
| |
| // proceed in case the connection is established and OK |
| if (isConnectionOK) |
| { |
| database = getDatabaseForProfile(profile); |
| |
| } |
| } |
| |
| return database; |
| } |
| |
| /** |
| * Given a Database file, provided by an {@link IPath}, in case |
| * it does not exist in the project´s asset´s directory, the file |
| * is to be copied to the mentioned directory. The project is represented |
| * by an {@link IProject}. |
| * |
| * @param databaseFilePath Database file path. |
| * @param targetProject Project in which the database is to be copied, in |
| * @param monitor Monitor for measuring the progress of the operation. |
| * case it does not exist. |
| * |
| * @throws FileNotFoundException Exception thrown in case the entered path points to an invalid path our file. |
| * @throws IllegalArgumentException Exception thrown in case the targetProject is null. |
| * @throws IOException Exception thrown when there are problems handling files in the copying process. |
| * @throws CoreException Exception thrown when there are problems creating the assets folder. |
| */ |
| public static void copyDatabaseFileToAssetsFolder(IPath databaseFilePath, |
| IProject targetProject, IProgressMonitor monitor) throws IOException, CoreException |
| { |
| // get sub monitor |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 10); |
| |
| // begin |
| subMonitor.beginTask(null, 10); |
| |
| // validate the path |
| if ((databaseFilePath == null) || !databaseFilePath.toFile().exists()) |
| { |
| throw new FileNotFoundException( |
| "The file entered by the databaseFilePath does not exists."); //$NON-NLS-1$ |
| } |
| |
| // validate the project |
| if (targetProject == null) |
| { |
| throw new IllegalArgumentException("The argument targetProject cannot be null."); //$NON-NLS-1$ |
| } |
| |
| // get database file |
| File databaseFile = databaseFilePath.toFile(); |
| |
| // get assets folder |
| IFolder assetsFolder = targetProject.getFolder(DatabaseUtils.ASSESTS_FOLDER); |
| |
| subMonitor.worked(3); |
| |
| if (assetsFolder.exists()) |
| { |
| // get the file matching the one entered on the Path |
| IResource foundDatabaseFile = assetsFolder.findMember(databaseFile.getName()); |
| subMonitor.worked(5); |
| // in case there is no file, or the resource is not a FILE, or the found file does not actually exists, copy it |
| if ((foundDatabaseFile == null) || (foundDatabaseFile.getType() != IResource.FILE) |
| || !foundDatabaseFile.exists()) |
| { |
| // copy the file |
| FileUtil.copyFile(databaseFile, assetsFolder.getFile(databaseFile.getName()) |
| .getLocation().toFile()); |
| // refresh assets |
| assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| } |
| } |
| else |
| { |
| // create the assets folder |
| assetsFolder.create(true, true, monitor); |
| subMonitor.worked(5); |
| // copy the file |
| FileUtil.copyFile(databaseFile, assetsFolder.getFile(databaseFile.getName()) |
| .getLocation().toFile()); |
| // refresh assets |
| assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| } |
| subMonitor.worked(10); |
| } |
| |
| /** |
| * Formats the code using the Eclipse Java settings if possible, |
| * otherwise returns original document not indented. |
| * @param destinationFile Destination file. |
| * @param databaseHelperText Text to generate. |
| * @param monitor A progress monitor to be used to show operation status. |
| * @return Created document. |
| */ |
| @SuppressWarnings( |
| { |
| "rawtypes", "unchecked" |
| }) |
| public static IDocument formatCode(IFile destinationFile, String databaseHelperText, |
| IProgressMonitor monitor) |
| { |
| IDocument document = new Document(); |
| File file = new File(destinationFile.getLocation().toOSString()); |
| |
| try |
| { |
| document.set(databaseHelperText); |
| |
| try |
| { |
| IJavaProject p = JavaCore.create(destinationFile.getProject()); |
| Map mapOptions = p.getOptions(true); |
| |
| TextEdit textEdit = |
| CodeFormatterUtil.format2(CodeFormatter.K_COMPILATION_UNIT |
| | CodeFormatter.F_INCLUDE_COMMENTS, document.get(), 0, |
| System.getProperty("line.separator"), mapOptions); |
| |
| if (textEdit != null) |
| { |
| textEdit.apply(document); |
| } |
| } |
| catch (Exception ex) |
| { |
| //do nothing |
| } |
| |
| BufferedWriter out = new BufferedWriter(new FileWriter(file)); |
| try |
| { |
| out.write(document.get()); |
| out.flush(); |
| } |
| finally |
| { |
| try |
| { |
| out.close(); |
| } |
| catch (IOException e) |
| { |
| /* ignore */ |
| } |
| } |
| // the refresh is needed in order to avoid the user to have to press F5 |
| destinationFile.getParent().refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| } |
| catch (Exception e) |
| { |
| String errMsg = |
| NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorFormattingSourceCode, |
| destinationFile.getName()); |
| StudioLogger.error(DatabaseUtils.class, errMsg, e); |
| } |
| return document; |
| |
| } |
| |
| } |