/*
* Copyright 2009-2016 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.jdt.groovy.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration;
import org.codehaus.jdt.groovy.internal.compiler.ast.JDTResolver;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.groovy.core.util.ReflectionUtils;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.JavaModelManager.PerWorkingCopyInfo;
import org.eclipse.jdt.internal.core.util.Util;
/**
* Stores module nodes for groovy compilation units This class is not meant to be accessed externally.
* <p>
* One module node is stored per working copy of a unit.
*
* @author Andrew Eisenberg
* @created Jun 11, 2009
*/
public class ModuleNodeMapper {
public static class ModuleNodeInfo {
public ModuleNodeInfo(ModuleNode module, JDTResolver resolver) {
this.module = module;
this.resolver = resolver;
}
public final ModuleNode module;
public CompilationResult result;
public final JDTResolver resolver;
public final boolean isEmpty() {
if (module == null || module.getClasses() == null || (module.getClasses().isEmpty() && module.getImports().isEmpty())) {
return true;
}
if (module.getClasses().size() == 1 && module.getImports().isEmpty() && module.getClasses().get(0).isScript()) {
if ((module.getStatementBlock() == null || module.getStatementBlock().isEmpty() || isNullReturn(module.getStatementBlock())) &&
(module.getMethods() == null || module.getMethods().isEmpty())) {
return true;
}
}
return false;
}
private static boolean isNullReturn(BlockStatement statementBlock) {
List<Statement> statements = statementBlock.getStatements();
if (statements.size() == 1 && statements.get(0) instanceof ReturnStatement) {
ReturnStatement ret = (ReturnStatement) statements.get(0);
if (ret.getExpression() instanceof ConstantExpression) {
return ((ConstantExpression) ret.getExpression()).isNullExpression();
}
}
return false;
}
}
private static final ModuleNodeMapper INSTANCE = new ModuleNodeMapper();
static ModuleNodeMapper getInstance() {
return INSTANCE;
}
private final ReentrantLock lock = new ReentrantLock(true);
private final Map<PerWorkingCopyInfo, ModuleNodeInfo> infoToModuleMap = new HashMap<PerWorkingCopyInfo, ModuleNodeInfo>();
void store(PerWorkingCopyInfo key, ModuleNodeInfo val) {
lock.lock();
try {
sweepAndPurgeModuleNodes();
infoToModuleMap.put(key, val);
} finally {
lock.unlock();
}
}
private final static boolean DSL_BUNDLE_INSTALLED;
static {
boolean result = false;
try {
result = Platform.getBundle("org.codehaus.groovy.eclipse.dsl") != null;
} catch (Exception e) {
Util.log(e);
}
DSL_BUNDLE_INSTALLED = result;
}
public static boolean shouldStoreResovler() {
return DSL_BUNDLE_INSTALLED;
}
ModuleNode getModule(PerWorkingCopyInfo info) {
lock.lock();
try {
ModuleNodeInfo moduleNodeInfo = get(info);
return moduleNodeInfo != null ? moduleNodeInfo.module : null;
} finally {
lock.unlock();
}
}
ModuleNodeInfo get(PerWorkingCopyInfo info) {
lock.lock();
try {
sweepAndPurgeModuleNodes();
return infoToModuleMap.get(info);
} finally {
lock.unlock();
}
}
JDTResolver getResolver(PerWorkingCopyInfo info) {
lock.lock();
try {
ModuleNodeInfo moduleNodeInfo = get(info);
return moduleNodeInfo != null ? moduleNodeInfo.resolver : null;
} finally {
lock.unlock();
}
}
ModuleNode remove(PerWorkingCopyInfo info) {
lock.lock();
try {
sweepAndPurgeModuleNodes();
ModuleNodeInfo removed = infoToModuleMap.remove(info);
return removed != null ? removed.module : null;
} finally {
lock.unlock();
}
}
/**
* Cache the module node if this is a working copy.
*/
protected void maybeCacheModuleNode(
final JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo,
final GroovyCompilationUnitDeclaration compilationUnitDeclaration) {
if (lock.tryLock()) {
try {
if (perWorkingCopyInfo != null && compilationUnitDeclaration != null) {
ModuleNode module = compilationUnitDeclaration.getModuleNode();
if (module != null) {
JDTResolver resolver = null;
if (shouldStoreResovler()) {
resolver = (JDTResolver) compilationUnitDeclaration.getCompilationUnit().getResolveVisitor();
}
ModuleNodeInfo info = new ModuleNodeInfo(module, resolver);
info.result = compilationUnitDeclaration.compilationResult();
store(perWorkingCopyInfo, info);
}
}
} finally {
lock.unlock();
}
} else {
// lock grabbed by someone else. rerun this operation later
new Job("Cache module node") {
@Override
protected IStatus run(IProgressMonitor monitor) {
maybeCacheModuleNode(perWorkingCopyInfo, compilationUnitDeclaration);
return Status.OK_STATUS;
}
}.schedule();
}
}
public static boolean isEmpty() {
return INSTANCE.infoToModuleMap.isEmpty();
}
public static int size() {
return INSTANCE.infoToModuleMap.size();
}
// GRECLIPSE-804 check to see that the stored nodes are correct
// provide info to stdout if not and purge any stale elements
void sweepAndPurgeModuleNodes() {
lock.lock();
try {
if (System.getProperty("groovy.eclipse.model.purge") == null) {
return;
}
List<PerWorkingCopyInfo> toPurge = new ArrayList<PerWorkingCopyInfo>();
for (PerWorkingCopyInfo info : infoToModuleMap.keySet()) {
int useCount = ((Integer) ReflectionUtils.getPrivateField(PerWorkingCopyInfo.class, "useCount", info)).intValue();
if (useCount <= 0) {
String message = "Bad module node map entry: " + info.getWorkingCopy().getElementName();
System.out.println(message);
Util.log(new RuntimeException(message), message);
toPurge.add(info);
} else if (useCount > 1) {
System.out.println(info.getWorkingCopy().getElementName() + " : useCount : " + useCount);
}
}
if (toPurge.size() > 0) {
for (PerWorkingCopyInfo info : toPurge) {
infoToModuleMap.remove(info);
}
}
} finally {
lock.unlock();
}
}
public void lock() {
lock.lock();
}
public void unlock() {
lock.unlock();
}
}