blob: 352cc0aac0766250942b9af3d9b51a146ae2b899 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.nls;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.osgi.util.NLS;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Region;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
/**
* calculates hints for the nls-refactoring out of a compilation unit.
* - package fragments of the accessor class and the resource bundle
* - accessor class name, resource bundle name
*/
public class NLSHint {
private String fAccessorName;
private IPackageFragment fAccessorPackage;
private String fResourceBundleName;
private IPackageFragment fResourceBundlePackage;
private NLSSubstitution[] fSubstitutions;
public NLSHint(ICompilationUnit cu, CompilationUnit astRoot) {
Assert.isNotNull(cu);
Assert.isNotNull(astRoot);
IPackageFragment cuPackage= (IPackageFragment) cu.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
fAccessorName= NLSRefactoring.DEFAULT_ACCESSOR_CLASSNAME;
fAccessorPackage= cuPackage;
fResourceBundleName= NLSRefactoring.DEFAULT_PROPERTY_FILENAME + NLSRefactoring.PROPERTY_FILE_EXT;
fResourceBundlePackage= cuPackage;
IJavaProject project= cu.getJavaProject();
NLSLine[] lines= createRawLines(cu);
AccessorClassReference accessClassRef= findFirstAccessorReference(lines, astRoot);
if (accessClassRef == null) {
// Look for Eclipse NLS approach
List<NLSLine> eclipseNLSLines= new ArrayList<>();
accessClassRef= createEclipseNLSLines(getDocument(cu), astRoot, eclipseNLSLines);
if (!eclipseNLSLines.isEmpty()) {
NLSLine[] rawLines= lines;
int rawLinesLength= rawLines.length;
int eclipseLinesLength= eclipseNLSLines.size();
lines= new NLSLine[rawLinesLength + eclipseLinesLength];
System.arraycopy(rawLines, 0, lines, 0, rawLinesLength);
for (int i= 0; i < eclipseLinesLength; i++)
lines[i+rawLinesLength]= eclipseNLSLines.get(i);
}
}
Properties props= null;
if (accessClassRef != null)
props= NLSHintHelper.getProperties(project, accessClassRef);
if (props == null)
props= new Properties();
fSubstitutions= createSubstitutions(lines, props, astRoot);
if (accessClassRef != null) {
fAccessorName= accessClassRef.getName();
ITypeBinding accessorClassBinding= accessClassRef.getBinding();
try {
IPackageFragment accessorPack= NLSHintHelper.getPackageOfAccessorClass(project, accessorClassBinding);
if (accessorPack != null) {
fAccessorPackage= accessorPack;
}
String fullBundleName= accessClassRef.getResourceBundleName();
if (fullBundleName != null) {
fResourceBundleName= Signature.getSimpleName(fullBundleName) + NLSRefactoring.PROPERTY_FILE_EXT;
String packName= Signature.getQualifier(fullBundleName);
IPackageFragment pack= NLSHintHelper.getResourceBundlePackage(project, packName, fResourceBundleName);
if (pack != null) {
fResourceBundlePackage= pack;
}
}
} catch (JavaModelException e) {
}
}
}
private AccessorClassReference createEclipseNLSLines(final IDocument document, CompilationUnit astRoot, List<NLSLine> nlsLines) {
final AccessorClassReference[] firstAccessor= new AccessorClassReference[1];
final SortedMap<Integer, NLSLine> lineToNLSLine= new TreeMap<>();
astRoot.accept(new ASTVisitor() {
private ICompilationUnit fCache_CU;
private CompilationUnit fCache_AST;
@Override
public boolean visit(QualifiedName node) {
ITypeBinding type= node.getQualifier().resolveTypeBinding();
if (type != null) {
ITypeBinding superType= type.getSuperclass();
if (superType != null && NLS.class.getName().equals(superType.getQualifiedName())) {
Integer line;
try {
line = Integer.valueOf(document.getLineOfOffset(node.getStartPosition()));
} catch (BadLocationException e) {
return true; // ignore and continue
}
NLSLine nlsLine= lineToNLSLine.get(line);
if (nlsLine == null) {
nlsLine= new NLSLine(line.intValue());
lineToNLSLine.put(line, nlsLine);
}
SimpleName name= node.getName();
NLSElement element= new NLSElement(node.getName().getIdentifier(), name.getStartPosition(),
name.getLength(), nlsLine.size() - 1, true);
nlsLine.add(element);
String bundleName;
ICompilationUnit bundleCU= (ICompilationUnit)type.getJavaElement().getAncestor(IJavaElement.COMPILATION_UNIT);
if (fCache_CU == null || !fCache_CU.equals(bundleCU) || fCache_AST == null) {
fCache_CU= bundleCU;
if (fCache_CU != null)
fCache_AST= SharedASTProviderCore.getAST(fCache_CU, SharedASTProviderCore.WAIT_YES, null);
else
fCache_AST= null;
}
bundleName= NLSHintHelper.getResourceBundleName(fCache_AST);
element.setAccessorClassReference(new AccessorClassReference(type, bundleName, new Region(node.getStartPosition(), node.getLength())));
if (firstAccessor[0] == null)
firstAccessor[0]= element.getAccessorClassReference();
}
}
return true;
}
});
nlsLines.addAll(lineToNLSLine.values());
return firstAccessor[0];
}
private IDocument getDocument(ICompilationUnit cu) {
IPath path= cu.getPath();
ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
try {
manager.connect(path, LocationKind.NORMALIZE, null);
} catch (CoreException e) {
return null;
}
try {
ITextFileBuffer buffer= manager.getTextFileBuffer(path, LocationKind.NORMALIZE);
if (buffer != null)
return buffer.getDocument();
} finally {
try {
manager.disconnect(path, LocationKind.NORMALIZE, null);
} catch (CoreException e) {
return null;
}
}
return null;
}
private NLSSubstitution[] createSubstitutions(NLSLine[] lines, Properties props, CompilationUnit astRoot) {
List<NLSSubstitution> result= new ArrayList<>();
for (NLSLine line : lines) {
for (NLSElement nlsElement : line.getElements()) {
if (nlsElement.hasTag()) {
AccessorClassReference accessorClassReference= NLSHintHelper.getAccessorClassReference(astRoot, nlsElement);
if (accessorClassReference == null) {
// no accessor class => not translated
result.add(new NLSSubstitution(NLSSubstitution.IGNORED, stripQuotes(nlsElement.getValue()), nlsElement));
} else {
String key= stripQuotes(nlsElement.getValue());
String value= props.getProperty(key);
result.add(new NLSSubstitution(NLSSubstitution.EXTERNALIZED, key, value, nlsElement, accessorClassReference));
}
} else if (nlsElement.isEclipseNLS()) {
String key= nlsElement.getValue();
result.add(new NLSSubstitution(NLSSubstitution.EXTERNALIZED, key, props.getProperty(key), nlsElement, nlsElement.getAccessorClassReference()));
} else {
result.add(new NLSSubstitution(NLSSubstitution.INTERNALIZED, stripQuotes(nlsElement.getValue()), nlsElement));
}
}
}
return result.toArray(new NLSSubstitution[result.size()]);
}
private static AccessorClassReference findFirstAccessorReference(NLSLine[] lines, CompilationUnit astRoot) {
for (NLSLine line : lines) {
for (NLSElement nlsElement : line.getElements()) {
if (nlsElement.hasTag()) {
AccessorClassReference accessorClassReference= NLSHintHelper.getAccessorClassReference(astRoot, nlsElement);
if (accessorClassReference != null) {
return accessorClassReference;
}
}
}
}
// try to find a access with missing //non-nls tag (bug 75155)
for (NLSLine line : lines) {
for (NLSElement nlsElement : line.getElements()) {
if (!nlsElement.hasTag()) {
AccessorClassReference accessorClassReference= NLSHintHelper.getAccessorClassReference(astRoot, nlsElement);
if (accessorClassReference != null) {
return accessorClassReference;
}
}
}
}
return null;
}
private static String stripQuotes(String str) {
return str.substring(1, str.length() - 1);
}
private static NLSLine[] createRawLines(ICompilationUnit cu) {
try {
return NLSScanner.scan(cu);
} catch (JavaModelException x) {
return new NLSLine[0];
} catch (InvalidInputException x) {
return new NLSLine[0];
} catch (BadLocationException x) {
return new NLSLine[0];
}
}
public String getAccessorClassName() {
return fAccessorName;
}
// public boolean isEclipseNLS() {
// return fIsEclipseNLS;
// }
public IPackageFragment getAccessorClassPackage() {
return fAccessorPackage;
}
public String getResourceBundleName() {
return fResourceBundleName;
}
public IPackageFragment getResourceBundlePackage() {
return fResourceBundlePackage;
}
public NLSSubstitution[] getSubstitutions() {
return fSubstitutions;
}
}