/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.codeassist.processors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.groovy.core.util.CharArraySequence;
import org.eclipse.jdt.groovy.core.util.JavaConstants;
import org.eclipse.jdt.ui.CodeStyleConfiguration;
public class GroovyImportRewriteFactory {
private static final Pattern IMPORTS_PATTERN = Pattern.compile("(\\A|[\\n\\r])import\\s");
private static final Pattern PACKAGE_PATTERN = Pattern.compile("(\\A|[\\n\\r])package\\s");
private static final Pattern EOL_PATTERN = Pattern.compile("($|[\\n\\r])");
private ImportRewrite rewrite;
// set to true if there is a problem creating the rewrite
private boolean cantCreateRewrite = false;
/**
* This should never be null
*/
private GroovyCompilationUnit unit;
/**
* This may be null
*/
private ModuleNode module;
/**
* If this constructor is used, the a check may be performed on the module
* for unrecoverable errors before generating an import rewrite. Compilation
* unit should never be null, although the module can be null
*/
public GroovyImportRewriteFactory(GroovyCompilationUnit unit, ModuleNode module) {
this.unit = unit;
this.module = module;
}
/**
* Module is null in this case. Only a compilation unit is passed.
*/
public GroovyImportRewriteFactory(GroovyCompilationUnit unit) {
this.unit = unit;
}
/**
* Returns an import rewrite for the module node only if
* ModuleNode.encounteredUnrecoverableError()
*
* Tries to find the start and end locations of the import statements. Makes
* a best guess using regular expression. This method ensures that even if
* the ComplationUnit is unparseable, the imports are still placed in the
* correct location.
*
* @return an {@link ImportRewrite} for the ModuleNode if it encountered an
* unrecoverable error, or null if no problems.
*/
public ImportRewrite getImportRewrite(IProgressMonitor monitor) {
// For the case of organize imports, if no unrecoverable error has been
// found,
// then we don't do any work here instead, a standard import rewrite is
// used.
// For the case of add import, this special rewrite will always be used
// (and the module field will be null)
if (module != null && !module.encounteredUnrecoverableError()) {
return null;
}
if (rewrite == null && !cantCreateRewrite) {
// find a reasonable substring that contains
// what looks to be the import dependencies
CharSequence imports = findImportsRegion(new CharArraySequence(unit.getContents()));
// Now send this to a parser
// need to be very careful here that if we can't parse, then don't send to rewriter
ASTParser parser = ASTParser.newParser(JavaConstants.AST_LEVEL);
parser.setSource(unit.cloneCachingContents(new StringBuilder(imports).append("class X { }").toString().toCharArray()));
parser.setKind(ASTParser.K_COMPILATION_UNIT);
ASTNode result = null;
try {
result = parser.createAST(monitor);
} catch (IllegalStateException e) {
GroovyContentAssist.logError("Can't create ImportRewrite for:\n" + imports, e);
}
if (result instanceof CompilationUnit) {
rewrite = CodeStyleConfiguration.createImportRewrite((CompilationUnit) result, true);
} else {
// something wierd happened.
// ensure we don't try again
cantCreateRewrite = true;
}
}
return rewrite;
}
/**
* Finds a region of text that kind of looks like where the imports should
* be placed. Uses regular expressions.
*
* @param contents the contents of a compilation unit
* @return a presumed region
*/
public static CharSequence findImportsRegion(CharSequence contents) {
// heuristics:
// look for last index of ^import
// if that returns -1, then look for ^package
Matcher matcher = IMPORTS_PATTERN.matcher(contents);
int importsEnd = 0;
while (matcher.find(importsEnd)) {
importsEnd = matcher.end();
}
if (importsEnd == 0) {
// no imports found, look for package declaration
matcher = PACKAGE_PATTERN.matcher(contents);
if (matcher.find()) {
importsEnd = matcher.end();
}
}
if (importsEnd > 0) {
// look for end of line
matcher = EOL_PATTERN.matcher(contents);
if (matcher.find(importsEnd)) {
importsEnd = matcher.end();
}
}
return contents.subSequence(0, importsEnd);
}
}