/* * 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.motorolamobility.studio.android.db.core.project; import java.io.File; import java.util.ArrayList; import java.util.List; 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.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.osgi.util.NLS; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.utilities.EclipseUtils; import com.motorolamobility.studio.android.db.core.DbCoreActivator; import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent; import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager; import com.motorolamobility.studio.android.db.core.exception.MotodevDbException; import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS; import com.motorolamobility.studio.android.db.core.model.DbModel; import com.motorolamobility.studio.android.db.core.model.TableModel; import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode; import com.motorolamobility.studio.android.db.core.ui.DbNode; import com.motorolamobility.studio.android.db.core.ui.IDbNode; import com.motorolamobility.studio.android.db.core.ui.ITreeNode; import com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode; /** * Implements the Project Node of the database tree view. * It listens for changes in project such as .db deletion/rename/addition that reflect in database model. */ public class ProjectNode extends AbstractTreeNode implements IDbCreatorNode, IResourceChangeListener { public static final String DB_FOLDER = "assets"; //$NON-NLS-1$ private IProject project; @SuppressWarnings("unused") private ProjectNode() { //Forcing user to use a proper constructor } public ProjectNode(IProject project, ITreeNode parent) { this(project.getName(), parent); this.project = project; } public ProjectNode(String id, ITreeNode parent) { super(parent); init(id); ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); } @Override public void refresh() { clear(); loadContent(); } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.ui.IDbCreatorNode#createDb(java.lang.String) */ public IStatus createDb(String dbName) { return createDb(dbName, null); } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.ui.IDbCreatorNode#createDb(java.lang.String, java.util.List) */ public IStatus createDb(String dbName, List<TableModel> tables) { IStatus status = Status.OK_STATUS; dbName = !dbName.endsWith(".db") ? dbName + ".db" : dbName; //$NON-NLS-1$ //$NON-NLS-2$ IPath projectPath = project.getLocation(); File assetsFolder = new File(projectPath.toFile(), "assets"); //$NON-NLS-1$ if (!assetsFolder.exists()) { assetsFolder.mkdirs(); } IPath dbPath = new Path(assetsFolder.getAbsolutePath()).append(File.separator + dbName); try { DbNode dbNode = new DbNode(dbPath, this, true); if (tables != null) { status = dbNode.createTables(tables); } putChild(dbNode); } catch (MotodevDbException e) { status = new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, NLS.bind( DbCoreNLS.ProjectNode_Error_While_Creating_DB, dbName), e); } return status; } private void init(String id) { setId(id); setName(id); } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon() */ @Override public ImageDescriptor getIcon() { return getSpecificIcon("com.android.ide.eclipse.adt", "icons/android.png"); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Loads the db files from the Android projects of the current workspace * @return list of db nodes */ private void loadContent() { File folder = ResourcesPlugin.getWorkspace().getRoot().getProject(getName()).getFile(DB_FOLDER) .getLocation().toFile(); if (folder.exists() && (folder.list().length > 0)) { File[] foundFiles = folder.listFiles(); List<ITreeNode> dbNodes = new ArrayList<ITreeNode>(foundFiles.length); for (File file : foundFiles) { if (DbModel.isValidSQLiteDatabase(file)) { DbNode dbNode; try { dbNode = new DbNode(new Path(file.getAbsolutePath()), this); dbNodes.add(dbNode); } catch (MotodevDbException e) { //Invalid db file found do nothing with it. } } } putChildren(dbNodes); } } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf() */ @Override public boolean isLeaf() { return false; } /** * @return the project */ protected IProject getProject() { return project; } /** * * @param file * @return * @throws MotodevDbException if dbPath does not contains a valid SQLite3 database file */ public void addDb(File file) { try { if (DbModel.isValidSQLiteDatabase(file)) { IDbNode dN = null; Path dbPath = new Path(file.getAbsolutePath()); dN = getDatabaseNodeFromFile(file); if (dN == null) { dN = new DbNode(dbPath, this); putChild(dN); } } } catch (MotodevDbException e) { String message = NLS.bind(DbCoreNLS.ProjectNode_Failed_ToVerify_If_DB_Is_Valid, file.getName()); StudioLogger.error(ProjectNode.class, message, e); } } /** * Deletes the database from filesystem */ public IStatus deleteDb(IDbNode dbNode) { IStatus status = dbNode.deleteDb(); if (status.isOK()) { removeDb(dbNode); } return status; } /** * Removes the node from database (e.g. when project is closed) * @param dbNode * @param dbName */ public void removeDb(IDbNode dbNode) { removeChild(dbNode); } protected void updateDatabase(File dbFile) { ITreeNode databaseNode = getDatabaseNodeFromFile(dbFile); if (databaseNode != null) { DatabaseModelEventManager.getInstance().fireEvent(databaseNode, DatabaseModelEvent.EVENT_TYPE.UPDATE); } } private IDbNode getDatabaseNodeFromFile(File dbFile) { Path dbFilePath = new Path(dbFile.getAbsolutePath()); String dbFileName = dbFilePath.lastSegment(); String id = dbFilePath.toFile().getParent() + "." + dbFileName; //$NON-NLS-1$ IDbNode databaseNode = null; ITreeNode node = getChildById(id); if (node instanceof IDbNode) { databaseNode = (IDbNode) node; } return databaseNode; } public void resourceChanged(IResourceChangeEvent event) { IResource res = event.getResource(); switch (event.getType()) { case IResourceChangeEvent.PRE_DELETE: //listener warns user about an opened database connection related to this project (when it tries to delete the project). //The user has to close the connection itself. boolean openedConnection = false; try { if (res instanceof IProject) { IProject project = (IProject) res; IFolder assetsFolder = project.getFolder(DB_FOLDER); if (assetsFolder.exists()) { IResource assetsRes[] = assetsFolder.members(); for (int i = 0; i < assetsRes.length; i++) { if (assetsRes[i] instanceof IFile) { IFile f = (IFile) assetsRes[i]; IDbNode dbNode = getDatabaseNodeFromFile(f.getLocation().toFile()); if (dbNode.isConnected()) { openedConnection = true; } } } } } } catch (Exception e) { //do nothing } if (openedConnection) { EclipseUtils.showWarningDialog(DbCoreNLS.UI_DeleteProjectDialogTitle, DbCoreNLS.UI_DeleteProjectDialogMsg); } break; case IResourceChangeEvent.POST_CHANGE: try { event.getDelta().accept(new ResourceDeltaVisior(this)); } catch (CoreException e) { StudioLogger.error(ProjectNode.class, "Error listening to changes in resources", e); //$NON-NLS-1$ } break; } } /** * Refresh assets folder under the android project. * @return true if success false otherwise. */ public boolean refreshAssetsFolder() { IFolder assetsFolder = this.project.getFolder(DB_FOLDER); if (assetsFolder.exists()) { try { assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e) { return false; } } return true; } class ResourceDeltaVisior implements IResourceDeltaVisitor { private final ProjectNode projectNode; ResourceDeltaVisior(ProjectNode projectNode) { this.projectNode = projectNode; } public boolean visit(IResourceDelta delta) { IResource res = delta.getResource(); if ((res instanceof IResource) && (res.getFileExtension() != null) && res.getProject().equals(projectNode.getProject()) && res.getFileExtension().equalsIgnoreCase("db")) //$NON-NLS-1$ { //avoid to add db to the wrong project - it may happen in case of copying db from one project to another switch (delta.getKind()) { case IResourceDelta.ADDED: StudioLogger.info("Database added: " + res.getFullPath()); //$NON-NLS-1$ addDb(res.getLocation().toFile()); break; case IResourceDelta.REMOVED: StudioLogger.info("Database deleted: " + res.getFullPath()); //$NON-NLS-1$ IDbNode dbNode = getDatabaseNodeFromFile(res.getLocation().toFile()); if (dbNode != null) { removeDb(dbNode); } break; } } return true; // visit the children } } /* (non-Javadoc) * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#clean() */ @Override public void cleanUp() { super.cleanUp(); ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } }