1 /******************************************************************************* 2 * Copyright (c) 2000, 2011 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.jdt.internal.corext.refactoring.nls; 15 16 import java.io.ByteArrayInputStream; 17 import java.io.IOException; 18 import java.io.InputStream; 19 import java.util.HashMap; 20 import java.util.Iterator; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.Properties; 24 25 import org.eclipse.core.runtime.CoreException; 26 27 import org.eclipse.core.resources.IFile; 28 import org.eclipse.core.resources.IStorage; 29 30 import org.eclipse.core.filebuffers.FileBuffers; 31 import org.eclipse.core.filebuffers.ITextFileBuffer; 32 import org.eclipse.core.filebuffers.ITextFileBufferManager; 33 import org.eclipse.core.filebuffers.LocationKind; 34 35 import org.eclipse.jface.text.IDocument; 36 import org.eclipse.jface.text.IRegion; 37 import org.eclipse.jface.text.Region; 38 39 import org.eclipse.jdt.core.ICompilationUnit; 40 import org.eclipse.jdt.core.IJavaElement; 41 import org.eclipse.jdt.core.IJavaProject; 42 import org.eclipse.jdt.core.IPackageFragment; 43 import org.eclipse.jdt.core.IPackageFragmentRoot; 44 import org.eclipse.jdt.core.IType; 45 import org.eclipse.jdt.core.ITypeRoot; 46 import org.eclipse.jdt.core.JavaModelException; 47 import org.eclipse.jdt.core.Signature; 48 import org.eclipse.jdt.core.dom.ASTNode; 49 import org.eclipse.jdt.core.dom.ASTVisitor; 50 import org.eclipse.jdt.core.dom.Assignment; 51 import org.eclipse.jdt.core.dom.CompilationUnit; 52 import org.eclipse.jdt.core.dom.Expression; 53 import org.eclipse.jdt.core.dom.IBinding; 54 import org.eclipse.jdt.core.dom.IMethodBinding; 55 import org.eclipse.jdt.core.dom.ITypeBinding; 56 import org.eclipse.jdt.core.dom.IVariableBinding; 57 import org.eclipse.jdt.core.dom.MethodInvocation; 58 import org.eclipse.jdt.core.dom.Modifier; 59 import org.eclipse.jdt.core.dom.Name; 60 import org.eclipse.jdt.core.dom.NodeFinder; 61 import org.eclipse.jdt.core.dom.QualifiedName; 62 import org.eclipse.jdt.core.dom.SimpleName; 63 import org.eclipse.jdt.core.dom.SimpleType; 64 import org.eclipse.jdt.core.dom.StringLiteral; 65 import org.eclipse.jdt.core.dom.TypeLiteral; 66 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 67 import org.eclipse.jdt.core.manipulation.SharedASTProviderCore; 68 69 import org.eclipse.jdt.internal.corext.dom.Bindings; 70 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 71 72 import org.eclipse.jdt.internal.ui.JavaPlugin; 73 74 public class NLSHintHelper { 75 NLSHintHelper()76 private NLSHintHelper() { 77 } 78 79 /** 80 * Returns the accessor binding info or <code>null</code> if this element is not a nls'ed entry 81 * 82 * @param astRoot the ast root 83 * @param nlsElement the nls element 84 * @return the accessor class reference or <code>null</code> if this element is not a nls'ed entry 85 */ getAccessorClassReference(CompilationUnit astRoot, NLSElement nlsElement)86 public static AccessorClassReference getAccessorClassReference(CompilationUnit astRoot, NLSElement nlsElement) { 87 IRegion region= nlsElement.getPosition(); 88 return getAccessorClassReference(astRoot, region); 89 } 90 91 /** 92 * Returns the accessor binding info or <code>null</code> if this element is not a nls'ed entry 93 * 94 * @param astRoot the ast root 95 * @param region the text region 96 * @return the accessor class reference or <code>null</code> if this element is not a nls'ed entry 97 */ getAccessorClassReference(CompilationUnit astRoot, IRegion region)98 public static AccessorClassReference getAccessorClassReference(CompilationUnit astRoot, IRegion region) { 99 return getAccessorClassReference(astRoot, region, false); 100 } 101 102 /** 103 * Returns the accessor binding info or <code>null</code> if this element is not a nls'ed entry 104 * 105 * @param astRoot the ast root 106 * @param region the text region 107 * @param usedFullyQualifiedName boolean flag to indicate that fully qualified name is used to 108 * refer a NLS key string constant 109 * @return the accessor class reference or <code>null</code> if this element is not a nls'ed 110 * entry 111 */ getAccessorClassReference(CompilationUnit astRoot, IRegion region, boolean usedFullyQualifiedName)112 public static AccessorClassReference getAccessorClassReference(CompilationUnit astRoot, IRegion region, boolean usedFullyQualifiedName) { 113 ASTNode nlsStringLiteral= NodeFinder.perform(astRoot, region.getOffset(), region.getLength()); 114 if (nlsStringLiteral == null) 115 return null; // not found 116 117 ASTNode parent= nlsStringLiteral.getParent(); 118 if (usedFullyQualifiedName) { 119 parent= parent.getParent(); 120 } 121 122 ITypeBinding accessorBinding= null; 123 124 if (!usedFullyQualifiedName && nlsStringLiteral instanceof SimpleName && nlsStringLiteral.getLocationInParent() == QualifiedName.NAME_PROPERTY) { 125 SimpleName name= (SimpleName)nlsStringLiteral; 126 127 IBinding binding= name.resolveBinding(); 128 if (binding instanceof IVariableBinding) { 129 IVariableBinding variableBinding= (IVariableBinding)binding; 130 if (Modifier.isStatic(variableBinding.getModifiers())) 131 accessorBinding= variableBinding.getDeclaringClass(); 132 } 133 } 134 135 if (accessorBinding == null) { 136 137 if (parent instanceof MethodInvocation) { 138 MethodInvocation methodInvocation= (MethodInvocation) parent; 139 List<Expression> args= methodInvocation.arguments(); 140 if (args.size() != 1 && args.indexOf(nlsStringLiteral) != 0) { 141 return null; // must be the only argument in lookup method 142 } 143 144 Expression firstArgument= args.get(0); 145 ITypeBinding argumentBinding= firstArgument.resolveTypeBinding(); 146 if (argumentBinding == null || !argumentBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$ 147 return null; 148 } 149 150 ITypeBinding typeBinding= methodInvocation.resolveTypeBinding(); 151 if (typeBinding == null || !typeBinding.getQualifiedName().equals("java.lang.String")) { //$NON-NLS-1$ 152 return null; 153 } 154 155 IMethodBinding methodBinding= methodInvocation.resolveMethodBinding(); 156 if (methodBinding == null || !Modifier.isStatic(methodBinding.getModifiers())) { 157 return null; // only static methods qualify 158 } 159 160 accessorBinding= methodBinding.getDeclaringClass(); 161 } else if (parent instanceof VariableDeclarationFragment) { 162 VariableDeclarationFragment decl= (VariableDeclarationFragment)parent; 163 if (decl.getInitializer() != null) 164 return null; 165 166 IBinding binding= decl.resolveBinding(); 167 if (!(binding instanceof IVariableBinding)) 168 return null; 169 170 IVariableBinding variableBinding= (IVariableBinding)binding; 171 if (!Modifier.isStatic(variableBinding.getModifiers())) 172 return null; 173 174 accessorBinding= variableBinding.getDeclaringClass(); 175 } 176 } 177 178 if (accessorBinding == null) 179 return null; 180 181 String resourceBundleName; 182 resourceBundleName= getResourceBundleName(accessorBinding); 183 184 if (resourceBundleName != null) 185 return new AccessorClassReference(accessorBinding, resourceBundleName, new Region(parent.getStartPosition(), parent.getLength())); 186 187 return null; 188 } 189 getPackageOfAccessorClass(IJavaProject javaProject, ITypeBinding accessorBinding)190 public static IPackageFragment getPackageOfAccessorClass(IJavaProject javaProject, ITypeBinding accessorBinding) throws JavaModelException { 191 if (accessorBinding != null) { 192 ICompilationUnit unit= Bindings.findCompilationUnit(accessorBinding, javaProject); 193 if (unit != null) { 194 return (IPackageFragment) unit.getParent(); 195 } 196 } 197 return null; 198 } 199 getResourceBundleName(ITypeBinding accessorClassBinding)200 public static String getResourceBundleName(ITypeBinding accessorClassBinding) { 201 IJavaElement je= accessorClassBinding.getJavaElement(); 202 if (!(je instanceof IType)) 203 return null; 204 ITypeRoot typeRoot= ((IType) je).getTypeRoot(); 205 CompilationUnit astRoot= SharedASTProviderCore.getAST(typeRoot, SharedASTProviderCore.WAIT_YES, null); 206 207 return getResourceBundleName(astRoot); 208 } 209 getResourceBundleName(ITypeRoot input)210 public static String getResourceBundleName(ITypeRoot input) { 211 return getResourceBundleName(SharedASTProviderCore.getAST(input, SharedASTProviderCore.WAIT_YES, null)); 212 } 213 getResourceBundleName(CompilationUnit astRoot)214 public static String getResourceBundleName(CompilationUnit astRoot) { 215 216 if (astRoot == null) 217 return null; 218 219 final Map<Object, Object> resultCollector= new HashMap<>(5); 220 final Object RESULT_KEY= new Object(); 221 final Object FIELD_KEY= new Object(); 222 223 astRoot.accept(new ASTVisitor() { 224 225 @Override 226 public boolean visit(MethodInvocation node) { 227 IMethodBinding method= node.resolveMethodBinding(); 228 if (method == null) 229 return true; 230 231 String name= method.getDeclaringClass().getQualifiedName(); 232 if ((!"java.util.ResourceBundle".equals(name) || !"getBundle".equals(method.getName()) || (node.arguments().size() <= 0)) && //old school //$NON-NLS-1$ //$NON-NLS-2$ 233 (!"org.eclipse.osgi.util.NLS".equals(name) || !"initializeMessages".equals(method.getName()) || (node.arguments().size() != 2))) //Eclipse style //$NON-NLS-1$ //$NON-NLS-2$ 234 return true; 235 236 Expression argument= (Expression)node.arguments().get(0); 237 String bundleName= getBundleName(argument); 238 if (bundleName != null) 239 resultCollector.put(RESULT_KEY, bundleName); 240 241 if (argument instanceof Name) { 242 Object fieldNameBinding= ((Name)argument).resolveBinding(); 243 if (fieldNameBinding != null) 244 resultCollector.put(FIELD_KEY, fieldNameBinding); 245 } 246 247 return false; 248 } 249 250 @Override 251 public boolean visit(VariableDeclarationFragment node) { 252 Expression initializer= node.getInitializer(); 253 String bundleName= getBundleName(initializer); 254 if (bundleName != null) { 255 Object fieldNameBinding= node.getName().resolveBinding(); 256 if (fieldNameBinding != null) 257 resultCollector.put(fieldNameBinding, bundleName); 258 return false; 259 } 260 return true; 261 } 262 263 @Override 264 public boolean visit(Assignment node) { 265 if (node.getLeftHandSide() instanceof Name) { 266 String bundleName= getBundleName(node.getRightHandSide()); 267 if (bundleName != null) { 268 Object fieldNameBinding= ((Name)node.getLeftHandSide()).resolveBinding(); 269 if (fieldNameBinding != null) { 270 resultCollector.put(fieldNameBinding, bundleName); 271 return false; 272 } 273 } 274 } 275 return true; 276 } 277 278 private String getBundleName(Expression initializer) { 279 if (initializer instanceof StringLiteral) 280 return ((StringLiteral)initializer).getLiteralValue(); 281 282 if (initializer instanceof MethodInvocation) { 283 MethodInvocation methInvocation= (MethodInvocation)initializer; 284 Expression exp= methInvocation.getExpression(); 285 if ((exp != null) && (exp instanceof TypeLiteral)) { 286 SimpleType simple= (SimpleType)((TypeLiteral) exp).getType(); 287 ITypeBinding typeBinding= simple.resolveBinding(); 288 if (typeBinding != null) 289 return typeBinding.getQualifiedName(); 290 } 291 } 292 return null; 293 } 294 295 }); 296 297 298 Object fieldName; 299 String result; 300 301 result= (String)resultCollector.get(RESULT_KEY); 302 if (result != null) 303 return result; 304 305 fieldName= resultCollector.get(FIELD_KEY); 306 if (fieldName != null) 307 return (String)resultCollector.get(fieldName); 308 309 // Now try hard-coded bundle name String field names from NLS tooling: 310 Iterator<Object> iter= resultCollector.keySet().iterator(); 311 while (iter.hasNext()) { 312 Object o= iter.next(); 313 if (!(o instanceof IBinding)) 314 continue; 315 IBinding binding= (IBinding)o; 316 fieldName= binding.getName(); 317 if (fieldName.equals("BUNDLE_NAME") || fieldName.equals("RESOURCE_BUNDLE") || fieldName.equals("bundleName")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 318 result= (String)resultCollector.get(binding); 319 if (result != null) 320 return result; 321 } 322 } 323 324 result= (String)resultCollector.get(RESULT_KEY); 325 if (result != null) 326 return result; 327 328 fieldName= resultCollector.get(FIELD_KEY); 329 if (fieldName != null) 330 return (String)resultCollector.get(fieldName); 331 332 return null; 333 } 334 getResourceBundlePackage(IJavaProject javaProject, String packageName, String resourceName)335 public static IPackageFragment getResourceBundlePackage(IJavaProject javaProject, String packageName, String resourceName) throws JavaModelException { 336 for (IPackageFragmentRoot root : javaProject.getAllPackageFragmentRoots()) { 337 if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { 338 IPackageFragment packageFragment= root.getPackageFragment(packageName); 339 if (packageFragment.exists()) { 340 for (Object object : packageFragment.isDefaultPackage() ? root.getNonJavaResources() : packageFragment.getNonJavaResources()) { 341 if (object instanceof IFile) { 342 IFile file= (IFile) object; 343 if (file.getName().equals(resourceName)) { 344 return packageFragment; 345 } 346 } 347 } 348 } 349 } 350 } 351 return null; 352 } 353 getResourceBundle(ICompilationUnit compilationUnit)354 public static IStorage getResourceBundle(ICompilationUnit compilationUnit) throws JavaModelException { 355 IJavaProject project= compilationUnit.getJavaProject(); 356 if (project == null) 357 return null; 358 359 String name= getResourceBundleName(compilationUnit); 360 if (name == null) 361 return null; 362 363 String packName= Signature.getQualifier(name); 364 String resourceName= Signature.getSimpleName(name) + NLSRefactoring.PROPERTY_FILE_EXT; 365 366 return getResourceBundle(project, packName, resourceName); 367 } 368 getResourceBundle(IJavaProject javaProject, String packageName, String resourceName)369 public static IStorage getResourceBundle(IJavaProject javaProject, String packageName, String resourceName) throws JavaModelException { 370 for (IPackageFragmentRoot root : javaProject.getAllPackageFragmentRoots()) { 371 if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { 372 IStorage storage= getResourceBundle(root, packageName, resourceName); 373 if (storage != null) 374 return storage; 375 } 376 } 377 return null; 378 } 379 getResourceBundle(IPackageFragmentRoot root, String packageName, String resourceName)380 public static IStorage getResourceBundle(IPackageFragmentRoot root, String packageName, String resourceName) throws JavaModelException { 381 IPackageFragment packageFragment= root.getPackageFragment(packageName); 382 if (packageFragment.exists()) { 383 for (Object object : packageFragment.isDefaultPackage() ? root.getNonJavaResources() : packageFragment.getNonJavaResources()) { 384 if (JavaModelUtil.isOpenableStorage(object)) { 385 IStorage storage= (IStorage)object; 386 if (storage.getName().equals(resourceName)) { 387 return storage; 388 } 389 } 390 } 391 } 392 return null; 393 } 394 getResourceBundle(IJavaProject javaProject, AccessorClassReference accessorClassReference)395 public static IStorage getResourceBundle(IJavaProject javaProject, AccessorClassReference accessorClassReference) throws JavaModelException { 396 String resourceBundle= accessorClassReference.getResourceBundleName(); 397 if (resourceBundle == null) 398 return null; 399 400 String resourceName= Signature.getSimpleName(resourceBundle) + NLSRefactoring.PROPERTY_FILE_EXT; 401 String packName= Signature.getQualifier(resourceBundle); 402 ITypeBinding accessorClass= accessorClassReference.getBinding(); 403 404 if (accessorClass.isFromSource()) 405 return getResourceBundle(javaProject, packName, resourceName); 406 else if (accessorClass.getJavaElement() != null) 407 return getResourceBundle((IPackageFragmentRoot)accessorClass.getJavaElement().getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT), packName, resourceName); 408 409 return null; 410 } 411 412 /** 413 * Reads the properties from the given storage and 414 * returns it. 415 * 416 * @param javaProject the Java project 417 * @param accessorClassReference the accessor class reference 418 * @return the properties or <code>null</code> if it was not successfully read 419 */ getProperties(IJavaProject javaProject, AccessorClassReference accessorClassReference)420 public static Properties getProperties(IJavaProject javaProject, AccessorClassReference accessorClassReference) { 421 try { 422 IStorage storage= NLSHintHelper.getResourceBundle(javaProject, accessorClassReference); 423 return getProperties(storage); 424 } catch (JavaModelException ex) { 425 // sorry no properties 426 return null; 427 } 428 } 429 430 /** 431 * Reads the properties from the given storage and 432 * returns it. 433 * 434 * @param storage the storage 435 * @return the properties or <code>null</code> if it was not successfully read 436 */ getProperties(IStorage storage)437 public static Properties getProperties(IStorage storage) { 438 if (storage == null) 439 return null; 440 441 Properties props= new Properties(); 442 InputStream is= null; 443 444 ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager(); 445 try { 446 if (manager != null) { 447 ITextFileBuffer buffer= manager.getTextFileBuffer(storage.getFullPath(), LocationKind.NORMALIZE); 448 if (buffer != null) { 449 IDocument document= buffer.getDocument(); 450 is= new ByteArrayInputStream(document.get().getBytes()); 451 } 452 } 453 454 // Fallback: read from storage 455 if (is == null) 456 is= storage.getContents(); 457 458 props.load(is); 459 460 } catch (IOException e) { 461 // sorry no properties 462 return null; 463 } catch (CoreException e) { 464 // sorry no properties 465 return null; 466 } finally { 467 if (is != null) try { 468 is.close(); 469 } catch (IOException e) { 470 // return properties anyway but log 471 JavaPlugin.log(e); 472 } 473 } 474 return props; 475 } 476 477 } 478