1 /*******************************************************************************
2  * Copyright (c) 2005, 2010 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.internal.ui.refactoring;
15 
16 import org.eclipse.core.filebuffers.*;
17 import org.eclipse.core.resources.*;
18 import org.eclipse.core.runtime.*;
19 import org.eclipse.jdt.core.*;
20 import org.eclipse.jface.text.IDocument;
21 import org.eclipse.ltk.core.refactoring.*;
22 import org.eclipse.pde.core.plugin.*;
23 import org.eclipse.pde.internal.core.ICoreConstants;
24 import org.eclipse.pde.internal.core.PDECore;
25 import org.eclipse.pde.internal.core.ischema.*;
26 import org.eclipse.pde.internal.core.schema.SchemaRegistry;
27 import org.eclipse.pde.internal.core.text.IDocumentAttributeNode;
28 import org.eclipse.pde.internal.core.text.plugin.*;
29 import org.eclipse.pde.internal.ui.util.PDEModelUtility;
30 import org.eclipse.text.edits.*;
31 
32 public class PluginManifestChange {
33 
createRenameChange(IFile file, Object[] affectedElements, String[] newNames, TextChange textChange, IProgressMonitor monitor)34 	public static Change createRenameChange(IFile file, Object[] affectedElements, String[] newNames, TextChange textChange, IProgressMonitor monitor) throws CoreException {
35 		ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager();
36 		try {
37 			manager.connect(file.getFullPath(), LocationKind.NORMALIZE, monitor);
38 			ITextFileBuffer buffer = manager.getTextFileBuffer(file.getFullPath(), LocationKind.NORMALIZE);
39 
40 			MultiTextEdit multiEdit = new MultiTextEdit();
41 
42 			IDocument document = buffer.getDocument();
43 
44 			try {
45 				PluginModelBase model;
46 				if (ICoreConstants.FRAGMENT_FILENAME_DESCRIPTOR.equals(file.getName()))
47 					model = new FragmentModel(document, false);
48 				else
49 					model = new PluginModel(document, false);
50 
51 				model.load();
52 				if (!model.isLoaded())
53 					return null;
54 
55 				for (int i = 0; i < affectedElements.length; i++) {
56 					if (model instanceof PluginModel && affectedElements[i] instanceof IJavaElement) {
57 						PluginNode plugin = (PluginNode) model.getPluginBase();
58 						IDocumentAttributeNode attr = plugin.getDocumentAttribute("class"); //$NON-NLS-1$
59 						TextEdit edit = createTextEdit(attr, (IJavaElement) affectedElements[i], newNames[i]);
60 						if (edit != null)
61 							multiEdit.addChild(edit);
62 					}
63 
64 					SchemaRegistry registry = PDECore.getDefault().getSchemaRegistry();
65 					IPluginExtension[] extensions = model.getPluginBase().getExtensions();
66 					for (IPluginExtension extension : extensions) {
67 						ISchema schema = registry.getSchema(extension.getPoint());
68 						if (schema != null)
69 							addExtensionAttributeEdit(schema, extension, multiEdit, affectedElements[i], newNames[i]);
70 					}
71 				}
72 
73 				if (multiEdit.hasChildren()) {
74 					// add to existing text edits.  If you create a new MultiText edit, the file will get corrupted since the edits are applied independently
75 					if (textChange != null) {
76 						TextEdit edit = textChange.getEdit();
77 						if (edit instanceof MultiTextEdit) {
78 							((MultiTextEdit) edit).addChild(multiEdit);
79 							multiEdit = ((MultiTextEdit) edit);
80 						} else
81 							multiEdit.addChild(edit);
82 					}
83 					TextFileChange change = new TextFileChange("", file); //$NON-NLS-1$
84 					change.setEdit(multiEdit);
85 					PDEModelUtility.setChangeTextType(change, file);
86 					return change;
87 				}
88 			} catch (CoreException e) {
89 				return null;
90 			}
91 			return null;
92 		} finally {
93 			manager.disconnect(file.getFullPath(), LocationKind.NORMALIZE, monitor);
94 		}
95 	}
96 
addExtensionAttributeEdit(ISchema schema, IPluginParent parent, MultiTextEdit multi, Object element, String newName)97 	private static void addExtensionAttributeEdit(ISchema schema, IPluginParent parent, MultiTextEdit multi, Object element, String newName) {
98 		IPluginObject[] children = parent.getChildren();
99 		for (IPluginObject childObject : children) {
100 			IPluginElement child = (IPluginElement) childObject;
101 			ISchemaElement schemaElement = schema.findElement(child.getName());
102 			if (schemaElement != null) {
103 				IPluginAttribute[] attributes = child.getAttributes();
104 				for (IPluginAttribute attr : attributes) {
105 					ISchemaAttribute attInfo = schemaElement.getAttribute(attr.getName());
106 					if (attInfo != null) {
107 						if (element instanceof IJavaElement && attInfo.getKind() == IMetaAttribute.JAVA) {
108 							IDocumentAttributeNode docAttr = (IDocumentAttributeNode) attr;
109 							TextEdit edit = createTextEdit(docAttr, (IJavaElement) element, newName);
110 							if (edit != null)
111 								multi.addChild(edit);
112 						} else if (element instanceof IResource && attInfo.getKind() == IMetaAttribute.RESOURCE) {
113 							IDocumentAttributeNode docAttr = (IDocumentAttributeNode) attr;
114 							TextEdit edit = createTextEdit(docAttr, (IResource) element, newName);
115 							if (edit != null)
116 								multi.addChild(edit);
117 						}
118 					}
119 				}
120 			}
121 			addExtensionAttributeEdit(schema, child, multi, element, newName);
122 		}
123 	}
124 
createTextEdit(IDocumentAttributeNode attr, IJavaElement element, String newName)125 	private static TextEdit createTextEdit(IDocumentAttributeNode attr, IJavaElement element, String newName) {
126 		if (attr == null)
127 			return null;
128 
129 		String oldName = (element instanceof IType) ? ((IType) element).getFullyQualifiedName('$') : element.getElementName();
130 		String value = attr.getAttributeValue();
131 		if (oldName.equals(value) || isGoodMatch(value, oldName, element instanceof IPackageFragment)) {
132 			int offset = attr.getValueOffset();
133 			if (offset >= 0)
134 				return new ReplaceEdit(offset, oldName.length(), newName);
135 		}
136 		return null;
137 	}
138 
createTextEdit(IDocumentAttributeNode attr, IResource resource, String newName)139 	private static TextEdit createTextEdit(IDocumentAttributeNode attr, IResource resource, String newName) {
140 		if (attr != null) {
141 			String oldName = resource.getProjectRelativePath().toString();
142 			String value = attr.getAttributeValue();
143 			if (oldName.equals(value) || ((resource instanceof IContainer) && isGoodFolderMatch(value, oldName))) {
144 				int offset = attr.getValueOffset();
145 				if (offset >= 0)
146 					return new ReplaceEdit(offset, oldName.length(), newName);
147 			}
148 		}
149 		return null;
150 	}
151 
isGoodMatch(String value, String oldName, boolean isPackage)152 	private static boolean isGoodMatch(String value, String oldName, boolean isPackage) {
153 		if (value == null || value.length() <= oldName.length())
154 			return false;
155 		boolean goodLengthMatch = isPackage ? value.lastIndexOf('.') <= oldName.length() : value.charAt(oldName.length()) == '$';
156 		return value.startsWith(oldName) && goodLengthMatch;
157 	}
158 
isGoodFolderMatch(String value, String oldName)159 	private static boolean isGoodFolderMatch(String value, String oldName) {
160 		return new Path(oldName).isPrefixOf(new Path(value));
161 	}
162 }
163