1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.refactorings.core;
18 
19 import static com.android.SdkConstants.ANDROID_URI;
20 import static com.android.SdkConstants.ATTR_NAME;
21 import static com.android.xml.AndroidManifest.ATTRIBUTE_BACKUP_AGENT;
22 import static com.android.xml.AndroidManifest.ATTRIBUTE_MANAGE_SPACE_ACTIVITY;
23 import static com.android.xml.AndroidManifest.ATTRIBUTE_PARENT_ACTIVITY_NAME;
24 import static com.android.xml.AndroidManifest.ATTRIBUTE_TARGET_ACTIVITY;
25 
26 import com.android.annotations.NonNull;
27 import com.android.annotations.Nullable;
28 import com.android.ide.eclipse.adt.AdtPlugin;
29 import com.android.xml.AndroidManifest;
30 
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IStatus;
33 import org.eclipse.jface.text.BadLocationException;
34 import org.eclipse.jface.text.IDocument;
35 import org.eclipse.wst.sse.core.StructuredModelManager;
36 import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
37 import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
38 import org.w3c.dom.Attr;
39 import org.w3c.dom.Element;
40 
41 import java.io.IOException;
42 import java.io.UnsupportedEncodingException;
43 
44 /**
45  * The utility class for android refactoring
46  *
47  */
48 @SuppressWarnings("restriction")
49 public class RefactoringUtil {
50 
51     private static boolean sRefactorAppPackage = false;
52 
53     /**
54      * Releases SSE read model; saves SSE model if exists edit model
55      * Called in dispose method of refactoring change classes
56      *
57      * @param model the SSE model
58      * @param document the document
59      */
fixModel(IStructuredModel model, IDocument document)60     public static void fixModel(IStructuredModel model, IDocument document) {
61         if (model != null) {
62             model.releaseFromRead();
63         }
64         model = null;
65         if (document == null) {
66             return;
67         }
68         try {
69             model = StructuredModelManager.getModelManager().getExistingModelForEdit(document);
70             if (model != null) {
71                 model.save();
72             }
73         } catch (UnsupportedEncodingException e1) {
74             // ignore
75         } catch (IOException e1) {
76             // ignore
77         } catch (CoreException e1) {
78             // ignore
79         } finally {
80             if (model != null) {
81                 model.releaseFromEdit();
82             }
83         }
84     }
85 
86     /**
87      * Logs the info message
88      *
89      * @param message the message
90      */
logInfo(String message)91     public static void logInfo(String message) {
92         AdtPlugin.log(IStatus.INFO, AdtPlugin.PLUGIN_ID, message);
93     }
94 
95     /**
96      * Logs the the exception
97      *
98      * @param e the exception
99      */
log(Throwable e)100     public static void log(Throwable e) {
101         AdtPlugin.log(e, e.getMessage());
102     }
103 
104     /**
105      * @return true if Rename/Move package needs to change the application package
106      * default is false
107      *
108      */
isRefactorAppPackage()109     public static boolean isRefactorAppPackage() {
110         return sRefactorAppPackage;
111     }
112 
113     /**
114      * @param refactorAppPackage true if Rename/Move package needs to change the application package
115      */
setRefactorAppPackage(boolean refactorAppPackage)116     public static void setRefactorAppPackage(boolean refactorAppPackage) {
117         RefactoringUtil.sRefactorAppPackage = refactorAppPackage;
118     }
119 
120     /**
121      * Returns the range of the attribute value in the given document
122      *
123      * @param attr the attribute to look up
124      * @param document the document containing the attribute
125      * @return the range of the value text, not including quotes, in the document
126      */
getAttributeValueRangeStart( @onNull Attr attr, @NonNull IDocument document)127     public static int getAttributeValueRangeStart(
128             @NonNull Attr attr,
129             @NonNull IDocument document) {
130         IndexedRegion region = (IndexedRegion) attr;
131         int potentialStart = attr.getName().length() + 2; // + 2: add ="
132         String text;
133         try {
134             text = document.get(region.getStartOffset(),
135                     region.getEndOffset() - region.getStartOffset());
136         } catch (BadLocationException e) {
137             return -1;
138         }
139         String value = attr.getValue();
140         int index = text.indexOf(value, potentialStart);
141         if (index != -1) {
142             return region.getStartOffset() + index;
143         } else {
144             return -1;
145         }
146     }
147 
148     /**
149      * Returns the start of the tag name of the given element
150      *
151      * @param element the element to look up
152      * @param document the document containing the attribute
153      * @return the index of the start tag in the document
154      */
getTagNameRangeStart( @onNull Element element, @NonNull IDocument document)155     public static int getTagNameRangeStart(
156             @NonNull Element element,
157             @NonNull IDocument document) {
158         IndexedRegion region = (IndexedRegion) element;
159         int potentialStart = 1; // add '<'
160         String text;
161         try {
162             text = document.get(region.getStartOffset(),
163                     region.getEndOffset() - region.getStartOffset());
164         } catch (BadLocationException e) {
165             return -1;
166         }
167         int index = text.indexOf(element.getTagName(), potentialStart);
168         if (index != -1) {
169             return region.getStartOffset() + index;
170         } else {
171             return -1;
172         }
173     }
174 
175     /**
176      * Returns whether the given manifest attribute should be considered to describe
177      * a class name. These will be eligible for refactoring when classes are renamed
178      * or moved.
179      *
180      * @param attribute the manifest attribute
181      * @return true if this attribute can describe a class
182      */
isManifestClassAttribute(@onNull Attr attribute)183     public static boolean isManifestClassAttribute(@NonNull Attr attribute) {
184         return isManifestClassAttribute(
185                 attribute.getOwnerElement().getTagName(),
186                 attribute.getNamespaceURI(),
187                 attribute.getLocalName());
188     }
189 
190     /**
191      * Returns whether the given manifest attribute should be considered to describe
192      * a class name. These will be eligible for refactoring when classes are renamed
193      * or moved.
194      *
195      * @param tag the tag, if known
196      * @param uri the attribute namespace, if any
197      * @param name the attribute local name, if any
198      * @return true if this attribute can describe a class
199      */
isManifestClassAttribute( @ullable String tag, @Nullable String uri, @Nullable String name)200     public static boolean isManifestClassAttribute(
201             @Nullable String tag,
202             @Nullable String uri,
203             @Nullable String name) {
204         if (name == null) {
205             return false;
206         }
207 
208         if ((name.equals(ATTR_NAME)
209                 && (AndroidManifest.NODE_ACTIVITY.equals(tag)
210                         || AndroidManifest.NODE_APPLICATION.equals(tag)
211                         || AndroidManifest.NODE_INSTRUMENTATION.equals(tag)
212                         || AndroidManifest.NODE_PROVIDER.equals(tag)
213                         || AndroidManifest.NODE_SERVICE.equals(tag)
214                         || AndroidManifest.NODE_RECEIVER.equals(tag)))
215                 || name.equals(ATTRIBUTE_TARGET_ACTIVITY)
216                 || name.equals(ATTRIBUTE_MANAGE_SPACE_ACTIVITY)
217                 || name.equals(ATTRIBUTE_BACKUP_AGENT)
218                 || name.equals(ATTRIBUTE_PARENT_ACTIVITY_NAME)) {
219             return ANDROID_URI.equals(uri);
220         }
221 
222         return false;
223     }
224 }
225