| /******************************************************************************* |
| * Copyright (c) 2004, 2011 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.common.snippets.internal.model; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.gef.palette.PaletteContainer; |
| import org.eclipse.wst.common.snippets.core.ISnippetCategory; |
| import org.eclipse.wst.common.snippets.core.ISnippetsEntry; |
| import org.eclipse.wst.common.snippets.internal.IEntryChangeListener; |
| import org.eclipse.wst.common.snippets.internal.ISnippetManager; |
| import org.eclipse.wst.common.snippets.internal.Logger; |
| import org.eclipse.wst.common.snippets.internal.SnippetDefinitions; |
| import org.eclipse.wst.common.snippets.internal.SnippetsPlugin; |
| import org.eclipse.wst.common.snippets.internal.palette.ModelFactoryForPlugins; |
| import org.eclipse.wst.common.snippets.internal.palette.ModelFactoryForUser; |
| import org.eclipse.wst.common.snippets.internal.palette.SnippetPaletteDrawer; |
| import org.eclipse.wst.common.snippets.internal.palette.SnippetPaletteItem; |
| import org.eclipse.wst.common.snippets.internal.palette.SnippetPaletteRoot; |
| import org.eclipse.wst.common.snippets.internal.palette.UserModelDumper; |
| import org.eclipse.wst.common.snippets.internal.palette.WorkspaceModelDumper; |
| import org.eclipse.wst.common.snippets.internal.team.CategoryFileInfo; |
| import org.eclipse.wst.common.snippets.internal.util.CommonXML; |
| import org.osgi.framework.Bundle; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| |
| /** |
| * |
| * SnippetManager |
| * |
| * Manages the Library model across multiple views |
| */ |
| public class SnippetManager implements ISnippetManager, PropertyChangeListener { |
| |
| private static final String STORAGE_FOLDER_NAME = "storage"; //$NON-NLS-1$ |
| |
| private static File hiddenStateFile = new File("/hidden.xml"); //$NON-NLS-1$ |
| |
| private static SnippetManager instance = null; |
| |
| public synchronized static SnippetManager getInstance() { |
| if (instance == null) { |
| instance = new SnippetManager(); |
| try { |
| hiddenStateFile = SnippetsPlugin.getDefault().getStateLocation().append("hidden.xml").toFile(); //$NON-NLS-1$ |
| } |
| catch (Exception e) { |
| hiddenStateFile = new File("/hidden.xml"); //$NON-NLS-1$ |
| } |
| |
| try { |
| File storageLocation = SnippetsPlugin.getDefault().getStateLocation().append(STORAGE_FOLDER_NAME).toFile(); |
| storageLocation.mkdirs(); |
| } |
| catch (SecurityException e) { |
| Logger.logException(e); |
| } |
| // hook resource listener and load categories from workspace |
| // nsd_TODO: disable in-workspace support until fully stabilized |
| // SnippetsPlugin.getDefault().initializeResourceChangeListener(); |
| } |
| return instance; |
| } |
| |
| private SnippetDefinitions definitions = null; |
| |
| protected IEntryChangeListener[] fListeners; |
| |
| private SnippetPaletteRoot fRoot; |
| |
| public SnippetManager() { |
| super(); |
| } |
| |
| public void addEntryChangeListener(IEntryChangeListener listener) { |
| if (fListeners == null) { |
| fListeners = new IEntryChangeListener[]{listener}; |
| } |
| else { |
| IEntryChangeListener[] newListeners = new IEntryChangeListener[fListeners.length + 1]; |
| newListeners[0] = listener; |
| System.arraycopy(fListeners, 0, newListeners, 1, fListeners.length); |
| fListeners = newListeners; |
| } |
| } |
| |
| protected ISnippetsEntry findEntry(SnippetDefinitions defs, String id) { |
| List categories = defs.getCategories(); |
| ISnippetsEntry match = null; |
| for (int i = 0; match == null && i < categories.size(); i++) { |
| SnippetPaletteDrawer category = (SnippetPaletteDrawer) categories.get(i); |
| if (category.getId().equals(id)) { |
| match = category; |
| } |
| else { |
| for (int j = 0; match == null && j < category.getChildren().size(); j++) { |
| SnippetPaletteItem item = (SnippetPaletteItem) category.getChildren().get(j); |
| if (item.getId().equals(id)) { |
| match = item; |
| } |
| } |
| } |
| } |
| return match; |
| } |
| |
| /** |
| * Find a ISnippetsEntry by it's ID field. Not at all efficient. |
| * |
| * @param id |
| * @return |
| */ |
| public ISnippetsEntry findEntry(String id) { |
| return findEntry(getDefinitions(), id); |
| } |
| |
| /** |
| * @param oldDefinitions |
| * @param newDefinitions |
| */ |
| private void fireModelChanged(SnippetDefinitions oldDefinitions, SnippetDefinitions newDefinitions) { |
| if (fListeners == null) |
| return; |
| for (int i = 0; i < fListeners.length; i++) |
| fListeners[i].modelChanged(oldDefinitions, newDefinitions); |
| } |
| |
| /** |
| * @return |
| */ |
| public SnippetDefinitions getDefinitions() { |
| if (definitions == null) { |
| synchronized (getInstance()) { |
| // load definitions |
| definitions = loadDefinitions(); |
| if (fRoot == null) { |
| fRoot = new SnippetPaletteRoot(definitions); |
| } |
| // hide categories so marked |
| String[] hiddenIDs = loadHiddenState(); |
| for (int i = 0; i < hiddenIDs.length; i++) { |
| ISnippetsEntry entry = findEntry(definitions, hiddenIDs[i]); |
| if (entry != null) { |
| if (entry instanceof SnippetPaletteItem) { |
| ((SnippetPaletteItem) entry).setVisible(false); |
| } |
| if (entry instanceof SnippetPaletteDrawer) { |
| ((SnippetPaletteDrawer) entry).setVisible(false); |
| } |
| } |
| } |
| } |
| } |
| return definitions; |
| } |
| |
| /** |
| * @return |
| */ |
| public IEntryChangeListener[] getListeners() { |
| return fListeners; |
| } |
| |
| public SnippetPaletteRoot getPaletteRoot() { |
| if (fRoot == null) { |
| fRoot = new SnippetPaletteRoot(getDefinitions()); |
| } |
| return fRoot; |
| } |
| |
| /** |
| * @return |
| */ |
| protected SnippetDefinitions loadDefinitions() { |
| // nsd_TODO: Ensure selection is constant across views |
| // (LibrarySelectionManager?) |
| SnippetDefinitions defs = new SnippetDefinitions(); |
| |
| // Load all of the definitions from their sources (probably could |
| // break |
| // this into extensions with providers) |
| SnippetDefinitions pluginDefs = ModelFactoryForPlugins.getInstance().loadCurrent(); |
| if (Logger.DEBUG_DEFINITION_PERSISTENCE) |
| System.out.println("SnippetManager loaded " + pluginDefs.getCategories().size() + " categories from plug-ins"); //$NON-NLS-1$ //$NON-NLS-2$ |
| SnippetDefinitions userDefs = ModelFactoryForUser.getInstance().loadCurrent(); |
| if (Logger.DEBUG_DEFINITION_PERSISTENCE) |
| System.out.println("SnippetManager loaded " + userDefs.getCategories().size() + " records for categories from user.xml"); //$NON-NLS-1$ //$NON-NLS-2$ |
| // SnippetDefinitions sharedDefs = |
| // ModelFactoryForWorkspace.getInstance().loadCurrent(); |
| // if (Logger.debugDefinitionPersistence) |
| // System.out.println("SnippetManager loaded " + |
| // userDefs.getCategories().size() + " records for categories from |
| // workspace"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| // the user categories also store placeholders for the non-user |
| // categories |
| defs.getCategories().addAll(userDefs.getCategories()); |
| |
| // Merge plugin drawers by replacing the placeholder drawers with the |
| // ones generated from the plugins |
| for (int i = 0; i < pluginDefs.getCategories().size(); i++) { |
| SnippetPaletteDrawer category = (SnippetPaletteDrawer) pluginDefs.getCategories().get(i); |
| String id = category.getId(); |
| ISnippetsEntry existingEntry = findEntry(defs, id); |
| int existingIndex = defs.getCategories().indexOf(existingEntry); |
| if (existingEntry != null && existingIndex > -1) { |
| // copy filters from existingEntry to pluginDefs category |
| category.setFilters(existingEntry.getFilters()); |
| defs.getCategories().set(existingIndex, category); |
| } |
| else { |
| defs.getCategories().add(category); |
| } |
| } |
| |
| // TODO: Merge workspace drawers |
| |
| // Remove stale plugin/workspace contributed drawers |
| // Place-holders are stored without children so any that are not |
| // refilled |
| // should be removed. Empty contributed drawers will be removed as |
| // well. |
| Iterator it = defs.getCategories().iterator(); |
| while (it.hasNext()) { |
| SnippetPaletteDrawer category = (SnippetPaletteDrawer) it.next(); |
| if (category.getSourceType() != ISnippetsEntry.SNIPPET_SOURCE_USER && category.getChildren().isEmpty()) |
| it.remove(); |
| } |
| |
| return defs; |
| } |
| |
| /** |
| * Load the list of categories that aren't to be visible |
| */ |
| protected String[] loadHiddenState() { |
| Document document = null; |
| String[] results = new String[0]; |
| try { |
| DocumentBuilder builder = CommonXML.getDocumentBuilder(); |
| if (builder != null) { |
| document = builder.parse(hiddenStateFile); |
| } |
| else { |
| Logger.log(Logger.ERROR, "SnippetManager couldn't obtain a DocumentBuilder"); //$NON-NLS-1$ |
| } |
| } |
| catch (FileNotFoundException e) { |
| // typical of new workspace, don't log it |
| document = null; |
| } |
| catch (IOException e) { |
| Logger.logException("SnippetManager could not load hidden state", e); //$NON-NLS-1$ |
| } |
| catch (SAXException e) { |
| Logger.logException("SnippetManager could not load hidden state", e); //$NON-NLS-1$ |
| } |
| if (document != null) { |
| List names = new ArrayList(0); |
| Element hidden = document.getDocumentElement(); |
| if (hidden != null) { |
| NodeList list = hidden.getChildNodes(); |
| for (int i = 0; i < list.getLength(); i++) { |
| Node childNode = list.item(i); |
| if (childNode.getNodeType() == Node.ELEMENT_NODE && childNode.getNodeName().equals(SnippetsPlugin.NAMES.HIDE)) { |
| Element entry = (Element) childNode; |
| String id = null; |
| if (entry.hasAttribute(SnippetsPlugin.NAMES.CATEGORY)) { |
| id = entry.getAttribute(SnippetsPlugin.NAMES.CATEGORY); |
| } |
| else if (entry.hasAttribute(SnippetsPlugin.NAMES.ITEM)) { |
| id = entry.getAttribute(SnippetsPlugin.NAMES.ITEM); |
| } |
| if (id != null && id.length() > 0) { |
| names.add(id); |
| } |
| } |
| } |
| results = (String[]) names.toArray(results); |
| } |
| } |
| if (Logger.DEBUG_DEFINITION_PERSISTENCE) |
| System.out.println("SnippetManager remembered " + results.length + " hidden categories"); //$NON-NLS-1$ //$NON-NLS-2$ |
| return results; |
| } |
| |
| /** |
| * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) |
| */ |
| public void propertyChange(PropertyChangeEvent evt) { |
| if (evt.getPropertyName().equals(PaletteContainer.PROPERTY_CHILDREN)) { |
| SnippetDefinitions oldDefinitions = getDefinitions(); |
| definitions = new SnippetDefinitions(); |
| definitions.setCategories((List) evt.getNewValue()); |
| fireModelChanged(oldDefinitions, definitions); |
| } |
| } |
| |
| public void removeEntryChangeListener(IEntryChangeListener listener) { |
| if (fListeners.length == 1) { |
| fListeners = null; |
| } |
| else { |
| List newListenersList = new ArrayList(Arrays.asList(fListeners)); |
| newListenersList.remove(listener); |
| IEntryChangeListener[] newListeners = new IEntryChangeListener[newListenersList.size() - 1]; |
| newListeners = (IEntryChangeListener[]) newListenersList.toArray(newListeners); |
| fListeners = newListeners; |
| } |
| } |
| |
| public void resetDefinitions() { |
| SnippetDefinitions oldDefinitions = definitions; |
| definitions = null; |
| if (fRoot != null) |
| fRoot.setDefinitions(getDefinitions()); |
| fireModelChanged(oldDefinitions, getDefinitions()); |
| } |
| |
| public synchronized void saveDefinitions() { |
| if (definitions == null) |
| return; |
| |
| // save the list of categories to not see |
| saveHiddenState(); |
| if (Logger.DEBUG_DEFINITION_PERSISTENCE) |
| System.out.println("SnippetManager saved hidden state"); //$NON-NLS-1$ |
| |
| // save the model |
| UserModelDumper.getInstance().write(getDefinitions()); |
| if (Logger.DEBUG_DEFINITION_PERSISTENCE) |
| System.out.println("SnippetManager wrote user records"); //$NON-NLS-1$ |
| List categories = getDefinitions().getCategories(); |
| final List workspaceCategories = new ArrayList(); |
| for (int i = 0; i < categories.size(); i++) { |
| ISnippetCategory category = (ISnippetCategory) categories.get(i); |
| if (category.getSourceType() == ISnippetsEntry.SNIPPET_SOURCE_WORKSPACE) { |
| CategoryFileInfo info = (CategoryFileInfo) category.getSourceDescriptor(); |
| if (info != null && info.getFile() != null) { |
| workspaceCategories.add(info); |
| } |
| } |
| } |
| for (int i = 0; i < workspaceCategories.size(); i++) { |
| CategoryFileInfo info = (CategoryFileInfo) workspaceCategories.get(i); |
| try { |
| if (Logger.DEBUG_DEFINITION_PERSISTENCE) |
| System.out.println("save workspace category: " + info.getCategory().getLabel()); //$NON-NLS-1$ |
| WorkspaceModelDumper.getInstance().write(info.getCategory(), info.getFile()); |
| } |
| catch (Exception e) { |
| Logger.logException(e); |
| } |
| } |
| } |
| |
| /** |
| * Save the list of categories that aren't visible |
| */ |
| protected void saveHiddenState() { |
| Document document = CommonXML.getDocumentBuilder().getDOMImplementation().createDocument(null, "hidden", null); //$NON-NLS-1$ |
| try { |
| FileOutputStream ostream = new FileOutputStream(hiddenStateFile); |
| List categoryIDs = new ArrayList(0); |
| List itemIDs = new ArrayList(0); |
| // collect all of the hidden entry IDs |
| for (int i = 0; i < getDefinitions().getCategories().size(); i++) { |
| SnippetPaletteDrawer category = (SnippetPaletteDrawer) getDefinitions().getCategories().get(i); |
| if (!category.isVisible() && !categoryIDs.contains(category.getId())) { |
| categoryIDs.add(category.getId()); |
| } |
| for (int j = 0; j < category.getChildren().size(); j++) { |
| SnippetPaletteItem entry = (SnippetPaletteItem) category.getChildren().get(j); |
| if (!entry.isVisible() && !itemIDs.contains(entry.getId())) { |
| itemIDs.add(entry.getId()); |
| } |
| } |
| } |
| // save those IDs in <hide category="id"/> tags |
| for (int j = 0; j < categoryIDs.size(); j++) { |
| Element hidden = document.createElement(SnippetsPlugin.NAMES.HIDE); |
| hidden.setAttribute(SnippetsPlugin.NAMES.CATEGORY, categoryIDs.get(j).toString()); |
| document.getDocumentElement().appendChild(hidden); |
| } |
| for (int j = 0; j < itemIDs.size(); j++) { |
| Element hidden = document.createElement(SnippetsPlugin.NAMES.HIDE); |
| hidden.setAttribute(SnippetsPlugin.NAMES.ITEM, itemIDs.get(j).toString()); |
| document.getDocumentElement().appendChild(hidden); |
| } |
| CommonXML.serialize(document, ostream); |
| ostream.close(); |
| } |
| catch (IOException e) { |
| Logger.logException(e); |
| } |
| } |
| |
| /** |
| * @param id |
| * @return the computed storage location for a snippet with this ID |
| */ |
| public IPath getStorageLocation(String id) { |
| Bundle bundle = Platform.getBundle(SnippetsPlugin.BUNDLE_ID); |
| return Platform.getStateLocation(bundle).append(STORAGE_FOLDER_NAME).addTrailingSeparator().append(id); |
| } |
| |
| /** |
| * Helper method for saving resources in designated snippet resource folder. |
| * |
| * @param snippetId - UID of the snippet that is saving content. |
| * @param pathList - Project relative path list. The resources are copied in the same structure |
| * in the storage path. e.g. having test.jsp file in project root will result in |
| * test.jsp in the storage root. If the jsp is in [Project Root]/resources/test.jsp |
| * this will result in [Snippet Storage Root]/resources/test.jsp |
| * @return |
| */ |
| public IStatus moveFilesInStorageFolder(IPath snippetStorage, IPath[] pathList, IProject project){ |
| File storageFolder = new File(snippetStorage.toOSString()); |
| if (!storageFolder.exists()){ |
| if (!storageFolder.mkdirs()){ |
| return createErrorStatus("Could not create snippet storage folder.", null); //$NON-NLS-1$ |
| } |
| } |
| for (int i = 0; i < pathList.length; i++) { |
| IFile file = project.getFile(pathList[i]); |
| IPath destPath = snippetStorage.append(pathList[i]); |
| File destination = new File(destPath.toOSString()); |
| if (!destination.getParentFile().exists()){ |
| if (!destination.getParentFile().mkdirs()){ |
| return createErrorStatus("Could not create snippet storage folder.", null); //$NON-NLS-1$ |
| } |
| } |
| try { |
| copy(file, destination); |
| } catch (FileNotFoundException e) { |
| return createErrorStatus(e.getLocalizedMessage(), e); |
| } catch (IOException e) { |
| return createErrorStatus(e.getLocalizedMessage(), e); |
| } catch (CoreException e) { |
| return e.getStatus(); |
| } |
| } |
| |
| return Status.OK_STATUS; |
| } |
| |
| public IStatus moveFilesInWorkspace(File[] fileList, IProject target, IPath storagePath) { |
| for (int i = 0; i < fileList.length; i++) { |
| String fileRelativePath = fileList[i].getAbsolutePath().substring(storagePath.toOSString().length()); |
| IFile ifile = target.getFile(new Path(fileRelativePath)); |
| try { |
| ifile.create(new FileInputStream(fileList[i]), true, new NullProgressMonitor()); |
| } catch (FileNotFoundException e) { |
| return createErrorStatus(e.getLocalizedMessage(), e); |
| } catch (CoreException e) { |
| return createErrorStatus(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| return Status.OK_STATUS; |
| } |
| |
| private void copy(IFile source, File destination) throws FileNotFoundException, IOException, CoreException{ |
| copy(source.getContents(), new FileOutputStream(destination)); |
| } |
| |
| private void copy(InputStream in, OutputStream out) throws IOException { |
| byte[] buffer = new byte[4096]; |
| try { |
| int n = in.read(buffer); |
| while (n > 0) { |
| out.write(buffer, 0, n); |
| n = in.read(buffer); |
| } |
| } finally { |
| in.close(); |
| out.close(); |
| } |
| } |
| |
| private IStatus createErrorStatus(String msg, Exception e) { |
| return new Status(IStatus.ERROR, SnippetsPlugin.BUNDLE_ID, msg, e); |
| } |
| |
| } |