1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.nls;
13 import java.io.ByteArrayInputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
20 import java.util.Properties;
22 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.resources.IFile;
25 import org.eclipse.core.resources.IStorage;
27 import org.eclipse.core.filebuffers.FileBuffers;
28 import org.eclipse.core.filebuffers.ITextFileBuffer;
29 import org.eclipse.core.filebuffers.ITextFileBufferManager;
30 import org.eclipse.core.filebuffers.LocationKind;
32 import org.eclipse.jface.text.IDocument;
33 import org.eclipse.jface.text.IRegion;
34 import org.eclipse.jface.text.Region;
36 import org.eclipse.jdt.core.ICompilationUnit;
37 import org.eclipse.jdt.core.IJavaElement;
38 import org.eclipse.jdt.core.IJavaProject;
39 import org.eclipse.jdt.core.IPackageFragment;
40 import org.eclipse.jdt.core.IPackageFragmentRoot;
41 import org.eclipse.jdt.core.IType;
42 import org.eclipse.jdt.core.ITypeRoot;
43 import org.eclipse.jdt.core.JavaModelException;
44 import org.eclipse.jdt.core.Signature;
45 import org.eclipse.jdt.core.dom.ASTNode;
46 import org.eclipse.jdt.core.dom.ASTVisitor;
47 import org.eclipse.jdt.core.dom.Assignment;
48 import org.eclipse.jdt.core.dom.CompilationUnit;
49 import org.eclipse.jdt.core.dom.Expression;
50 import org.eclipse.jdt.core.dom.IBinding;
51 import org.eclipse.jdt.core.dom.IMethodBinding;
52 import org.eclipse.jdt.core.dom.ITypeBinding;
53 import org.eclipse.jdt.core.dom.IVariableBinding;
54 import org.eclipse.jdt.core.dom.MethodInvocation;
55 import org.eclipse.jdt.core.dom.Modifier;
56 import org.eclipse.jdt.core.dom.Name;
57 import org.eclipse.jdt.core.dom.NodeFinder;
58 import org.eclipse.jdt.core.dom.QualifiedName;
59 import org.eclipse.jdt.core.dom.SimpleName;
60 import org.eclipse.jdt.core.dom.SimpleType;
61 import org.eclipse.jdt.core.dom.StringLiteral;
62 import org.eclipse.jdt.core.dom.TypeLiteral;
63 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
65 import org.eclipse.jdt.internal.corext.dom.Bindings;
66 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
68 import org.eclipse.jdt.ui.SharedASTProvider;
70 import org.eclipse.jdt.internal.ui.JavaPlugin;
72 public class NLSHintHelper {
74 private NLSHintHelper() {
78 * Returns the accessor binding info or <code>null</code> if this element is not a nls'ed entry
80 * @param astRoot the ast root
81 * @param nlsElement the nls element
82 * @return the accessor class reference or <code>null</code> if this element is not a nls'ed entry
84 public static AccessorClassReference getAccessorClassReference(CompilationUnit astRoot, NLSElement nlsElement) {
85 IRegion region= nlsElement.getPosition();
86 return getAccessorClassReference(astRoot, region);
90 * Returns the accessor binding info or <code>null</code> if this element is not a nls'ed entry
92 * @param astRoot the ast root
93 * @param region the text region
94 * @return the accessor class reference or <code>null</code> if this element is not a nls'ed entry
96 public static AccessorClassReference getAccessorClassReference(CompilationUnit astRoot, IRegion region) {
97 return getAccessorClassReference(astRoot, region, false);
101 * Returns the accessor binding info or <code>null</code> if this element is not a nls'ed entry
103 * @param astRoot the ast root
104 * @param region the text region
105 * @param usedFullyQualifiedName boolean flag to indicate that fully qualified name is used to
106 * refer a NLS key string constant
107 * @return the accessor class reference or <code>null</code> if this element is not a nls'ed
110 public static AccessorClassReference getAccessorClassReference(CompilationUnit astRoot, IRegion region, boolean usedFullyQualifiedName) {
111 ASTNode nlsStringLiteral= NodeFinder.perform(astRoot, region.getOffset(), region.getLength());
112 if (nlsStringLiteral == null)
113 return null; // not found
115 ASTNode parent= nlsStringLiteral.getParent();
116 if (usedFullyQualifiedName) {
117 parent= parent.getParent();
120 ITypeBinding accessorBinding= null;
122 if (!usedFullyQualifiedName && nlsStringLiteral instanceof SimpleName && nlsStringLiteral.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
123 SimpleName name= (SimpleName)nlsStringLiteral;
125 IBinding binding= name.resolveBinding();
126 if (binding instanceof IVariableBinding) {
127 IVariableBinding variableBinding= (IVariableBinding)binding;
128 if (Modifier.isStatic(variableBinding.getModifiers()))
129 accessorBinding= variableBinding.getDeclaringClass();
133 if (accessorBinding == null) {
135 if (parent instanceof MethodInvocation) {
136 MethodInvocation methodInvocation= (MethodInvocation) parent;
137 List<Expression> args= methodInvocation.arguments();
138 if (args.size() != 1 && args.indexOf(nlsStringLiteral) != 0) {
139 return null; // must be the only argument in lookup method
142 Expression firstArgument= args.get(0);
143 ITypeBinding argumentBinding= firstArgument.resolveTypeBinding();
144 if (argumentBinding == null || !argumentBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$
148 ITypeBinding typeBinding= methodInvocation.resolveTypeBinding();
149 if (typeBinding == null || !typeBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$
153 IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
154 if (methodBinding == null || !Modifier.isStatic(methodBinding.getModifiers())) {
155 return null; // only static methods qualify
158 accessorBinding= methodBinding.getDeclaringClass();
159 } else if (parent instanceof VariableDeclarationFragment) {
160 VariableDeclarationFragment decl= (VariableDeclarationFragment)parent;
161 if (decl.getInitializer() != null)
164 IBinding binding= decl.resolveBinding();
165 if (!(binding instanceof IVariableBinding))
168 IVariableBinding variableBinding= (IVariableBinding)binding;
169 if (!Modifier.isStatic(variableBinding.getModifiers()))
172 accessorBinding= variableBinding.getDeclaringClass();
176 if (accessorBinding == null)
179 String resourceBundleName;
180 resourceBundleName= getResourceBundleName(accessorBinding);
182 if (resourceBundleName != null)
183 return new AccessorClassReference(accessorBinding, resourceBundleName, new Region(parent.getStartPosition(), parent.getLength()));
188 public static IPackageFragment getPackageOfAccessorClass(IJavaProject javaProject, ITypeBinding accessorBinding) throws JavaModelException {
189 if (accessorBinding != null) {
190 ICompilationUnit unit= Bindings.findCompilationUnit(accessorBinding, javaProject);
192 return (IPackageFragment) unit.getParent();
198 public static String getResourceBundleName(ITypeBinding accessorClassBinding) {
199 IJavaElement je= accessorClassBinding.getJavaElement();
200 if (!(je instanceof IType))
202 ITypeRoot typeRoot= ((IType) je).getTypeRoot();
203 CompilationUnit astRoot= SharedASTProvider.getAST(typeRoot, SharedASTProvider.WAIT_YES, null);
205 return getResourceBundleName(astRoot);
208 public static String getResourceBundleName(ITypeRoot input) {
209 return getResourceBundleName(SharedASTProvider.getAST(input, SharedASTProvider.WAIT_YES, null));
212 public static String getResourceBundleName(CompilationUnit astRoot) {
217 final Map<Object, Object> resultCollector= new HashMap<Object, Object>(5);
218 final Object RESULT_KEY= new Object();
219 final Object FIELD_KEY= new Object();
221 astRoot.accept(new ASTVisitor() {
224 public boolean visit(MethodInvocation node) {
225 IMethodBinding method= node.resolveMethodBinding();
229 String name= method.getDeclaringClass().getQualifiedName();
230 if (!("java.util.ResourceBundle".equals(name) && "getBundle".equals(method.getName()) && node.arguments().size() > 0) && //old school //$NON-NLS-1$ //$NON-NLS-2$
231 !("org.eclipse.osgi.util.NLS".equals(name) && "initializeMessages".equals(method.getName()) && node.arguments().size() == 2)) //Eclipse style //$NON-NLS-1$ //$NON-NLS-2$
234 Expression argument= (Expression)node.arguments().get(0);
235 String bundleName= getBundleName(argument);
236 if (bundleName != null)
237 resultCollector.put(RESULT_KEY, bundleName);
239 if (argument instanceof Name) {
240 Object fieldNameBinding= ((Name)argument).resolveBinding();
241 if (fieldNameBinding != null)
242 resultCollector.put(FIELD_KEY, fieldNameBinding);
249 public boolean visit(VariableDeclarationFragment node) {
250 Expression initializer= node.getInitializer();
251 String bundleName= getBundleName(initializer);
252 if (bundleName != null) {
253 Object fieldNameBinding= node.getName().resolveBinding();
254 if (fieldNameBinding != null)
255 resultCollector.put(fieldNameBinding, bundleName);
262 public boolean visit(Assignment node) {
263 if (node.getLeftHandSide() instanceof Name) {
264 String bundleName= getBundleName(node.getRightHandSide());
265 if (bundleName != null) {
266 Object fieldNameBinding= ((Name)node.getLeftHandSide()).resolveBinding();
267 if (fieldNameBinding != null) {
268 resultCollector.put(fieldNameBinding, bundleName);
276 private String getBundleName(Expression initializer) {
277 if (initializer instanceof StringLiteral)
278 return ((StringLiteral)initializer).getLiteralValue();
280 if (initializer instanceof MethodInvocation) {
281 MethodInvocation methInvocation= (MethodInvocation)initializer;
282 Expression exp= methInvocation.getExpression();
283 if ((exp != null) && (exp instanceof TypeLiteral)) {
284 SimpleType simple= (SimpleType)((TypeLiteral) exp).getType();
285 ITypeBinding typeBinding= simple.resolveBinding();
286 if (typeBinding != null)
287 return typeBinding.getQualifiedName();
299 result= (String)resultCollector.get(RESULT_KEY);
303 fieldName= resultCollector.get(FIELD_KEY);
304 if (fieldName != null)
305 return (String)resultCollector.get(fieldName);
307 // Now try hard-coded bundle name String field names from NLS tooling:
308 Iterator<Object> iter= resultCollector.keySet().iterator();
309 while (iter.hasNext()) {
310 Object o= iter.next();
311 if (!(o instanceof IBinding))
313 IBinding binding= (IBinding)o;
314 fieldName= binding.getName();
315 if (fieldName.equals("BUNDLE_NAME") || fieldName.equals("RESOURCE_BUNDLE") || fieldName.equals("bundleName")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
316 result= (String)resultCollector.get(binding);
322 result= (String)resultCollector.get(RESULT_KEY);
326 fieldName= resultCollector.get(FIELD_KEY);
327 if (fieldName != null)
328 return (String)resultCollector.get(fieldName);
333 public static IPackageFragment getResourceBundlePackage(IJavaProject javaProject, String packageName, String resourceName) throws JavaModelException {
334 IPackageFragmentRoot[] allRoots= javaProject.getAllPackageFragmentRoots();
335 for (int i= 0; i < allRoots.length; i++) {
336 IPackageFragmentRoot root= allRoots[i];
337 if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
338 IPackageFragment packageFragment= root.getPackageFragment(packageName);
339 if (packageFragment.exists()) {
340 Object[] resources= packageFragment.isDefaultPackage() ? root.getNonJavaResources() : packageFragment.getNonJavaResources();
341 for (int j= 0; j < resources.length; j++) {
342 Object object= resources[j];
343 if (object instanceof IFile) {
344 IFile file= (IFile) object;
345 if (file.getName().equals(resourceName)) {
346 return packageFragment;
356 public static IStorage getResourceBundle(ICompilationUnit compilationUnit) throws JavaModelException {
357 IJavaProject project= compilationUnit.getJavaProject();
361 String name= getResourceBundleName(compilationUnit);
365 String packName= Signature.getQualifier(name);
366 String resourceName= Signature.getSimpleName(name) + NLSRefactoring.PROPERTY_FILE_EXT;
368 return getResourceBundle(project, packName, resourceName);
371 public static IStorage getResourceBundle(IJavaProject javaProject, String packageName, String resourceName) throws JavaModelException {
372 IPackageFragmentRoot[] allRoots= javaProject.getAllPackageFragmentRoots();
373 for (int i= 0; i < allRoots.length; i++) {
374 IPackageFragmentRoot root= allRoots[i];
375 if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
376 IStorage storage= getResourceBundle(root, packageName, resourceName);
384 public static IStorage getResourceBundle(IPackageFragmentRoot root, String packageName, String resourceName) throws JavaModelException {
385 IPackageFragment packageFragment= root.getPackageFragment(packageName);
386 if (packageFragment.exists()) {
387 Object[] resources= packageFragment.isDefaultPackage() ? root.getNonJavaResources() : packageFragment.getNonJavaResources();
388 for (int j= 0; j < resources.length; j++) {
389 Object object= resources[j];
390 if (JavaModelUtil.isOpenableStorage(object)) {
391 IStorage storage= (IStorage)object;
392 if (storage.getName().equals(resourceName)) {
401 public static IStorage getResourceBundle(IJavaProject javaProject, AccessorClassReference accessorClassReference) throws JavaModelException {
402 String resourceBundle= accessorClassReference.getResourceBundleName();
403 if (resourceBundle == null)
406 String resourceName= Signature.getSimpleName(resourceBundle) + NLSRefactoring.PROPERTY_FILE_EXT;
407 String packName= Signature.getQualifier(resourceBundle);
408 ITypeBinding accessorClass= accessorClassReference.getBinding();
410 if (accessorClass.isFromSource())
411 return getResourceBundle(javaProject, packName, resourceName);
412 else if (accessorClass.getJavaElement() != null)
413 return getResourceBundle((IPackageFragmentRoot)accessorClass.getJavaElement().getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT), packName, resourceName);
419 * Reads the properties from the given storage and
422 * @param javaProject the Java project
423 * @param accessorClassReference the accessor class reference
424 * @return the properties or <code>null</code> if it was not successfully read
426 public static Properties getProperties(IJavaProject javaProject, AccessorClassReference accessorClassReference) {
428 IStorage storage= NLSHintHelper.getResourceBundle(javaProject, accessorClassReference);
429 return getProperties(storage);
430 } catch (JavaModelException ex) {
431 // sorry no properties
437 * Reads the properties from the given storage and
440 * @param storage the storage
441 * @return the properties or <code>null</code> if it was not successfully read
443 public static Properties getProperties(IStorage storage) {
447 Properties props= new Properties();
448 InputStream is= null;
450 ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
452 if (manager != null) {
453 ITextFileBuffer buffer= manager.getTextFileBuffer(storage.getFullPath(), LocationKind.NORMALIZE);
454 if (buffer != null) {
455 IDocument document= buffer.getDocument();
456 is= new ByteArrayInputStream(document.get().getBytes());
460 // Fallback: read from storage
462 is= storage.getContents();
466 } catch (IOException e) {
467 // sorry no properties
469 } catch (CoreException e) {
470 // sorry no properties
473 if (is != null) try {
475 } catch (IOException e) {
476 // return properties anyway but log