blob: f1af2aa27a3a4d4bbae1c3fc638f6379dcf5f162 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.jetbrains.plugins.groovy.annotator.intentions.dynamic;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.components.StorageScheme;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiModificationTrackerImpl;
import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.annotator.intentions.QuickfixUtil;
import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.elements.*;
import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.ui.DynamicElementSettings;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* User: Dmitry.Krasilschikov
* Date: 23.11.2007
*/
@State(
name = "DynamicElementsStorage",
storages = {
@Storage(file = StoragePathMacros.PROJECT_FILE),
@Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/dynamic.xml", scheme = StorageScheme.DIRECTORY_BASED)
})
public class DynamicManagerImpl extends DynamicManager {
private final Project myProject;
private DRootElement myRootElement = new DRootElement();
public DynamicManagerImpl(final Project project) {
myProject = project;
StartupManager.getInstance(project).registerPostStartupActivity(new Runnable() {
@Override
public void run() {
if (!myRootElement.getContainingClasses().isEmpty()) {
DynamicToolWindowWrapper.getInstance(project).getToolWindow(); //initialize myToolWindow
}
}
});
}
public Project getProject() {
return myProject;
}
@Override
public void initComponent() {
}
@Override
public void addProperty(DynamicElementSettings settings) {
assert settings != null;
assert !settings.isMethod();
final DPropertyElement propertyElement = (DPropertyElement) createDynamicElement(settings);
final DClassElement classElement = getOrCreateClassElement(myProject, settings.getContainingClassName());
ToolWindow window = DynamicToolWindowWrapper.getInstance(myProject).getToolWindow(); //important to fetch myToolWindow before adding
classElement.addProperty(propertyElement);
addItemInTree(classElement, propertyElement, window);
}
private void removeItemFromTree(DItemElement itemElement, DClassElement classElement) {
DynamicToolWindowWrapper wrapper = DynamicToolWindowWrapper.getInstance(myProject);
ListTreeTableModelOnColumns model = wrapper.getTreeTableModel();
Object classNode = TreeUtil.findNodeWithObject(classElement, model, model.getRoot());
final DefaultMutableTreeNode node = (DefaultMutableTreeNode) TreeUtil.findNodeWithObject(itemElement, model, classNode);
if (node == null) return;
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
doRemove(wrapper, node, parent);
}
private void removeClassFromTree(DClassElement classElement) {
DynamicToolWindowWrapper wrapper = DynamicToolWindowWrapper.getInstance(myProject);
ListTreeTableModelOnColumns model = wrapper.getTreeTableModel();
final DefaultMutableTreeNode node = (DefaultMutableTreeNode) TreeUtil.findNodeWithObject(classElement, model, model.getRoot());
if (node == null) return;
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
doRemove(wrapper, node, parent);
}
private static void doRemove(DynamicToolWindowWrapper wrapper, DefaultMutableTreeNode node, DefaultMutableTreeNode parent) {
DefaultMutableTreeNode toSelect = (parent.getChildAfter(node) != null || parent.getChildCount() == 1 ?
node.getNextNode() :
node.getPreviousNode());
wrapper.removeFromParent(parent, node);
if (toSelect != null) {
wrapper.setSelectedNode(toSelect);
}
}
private void addItemInTree(final DClassElement classElement, final DItemElement itemElement, final ToolWindow window) {
final ListTreeTableModelOnColumns myTreeTableModel = DynamicToolWindowWrapper.getInstance(myProject).getTreeTableModel();
window.activate(new Runnable() {
@Override
public void run() {
final Object rootObject = myTreeTableModel.getRoot();
if (!(rootObject instanceof DefaultMutableTreeNode)) return;
final DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) rootObject;
DefaultMutableTreeNode node = new DefaultMutableTreeNode(itemElement);
if (rootNode.getChildCount() > 0) {
for (DefaultMutableTreeNode classNode = (DefaultMutableTreeNode) rootNode.getFirstChild();
classNode != null;
classNode = (DefaultMutableTreeNode) rootNode.getChildAfter(classNode)) {
final Object classRow = classNode.getUserObject();
if (!(classRow instanceof DClassElement)) return;
DClassElement otherClassName = (DClassElement) classRow;
if (otherClassName.equals(classElement)) {
int index = getIndexToInsert(classNode, itemElement);
classNode.insert(node, index);
myTreeTableModel.nodesWereInserted(classNode, new int[]{index});
DynamicToolWindowWrapper.getInstance(myProject).setSelectedNode(node);
return;
}
}
}
// if there is no such class in tree
int index = getIndexToInsert(rootNode, classElement);
DefaultMutableTreeNode classNode = new DefaultMutableTreeNode(classElement);
rootNode.insert(classNode, index);
myTreeTableModel.nodesWereInserted(rootNode, new int[]{index});
classNode.add(node);
myTreeTableModel.nodesWereInserted(classNode, new int[]{0});
DynamicToolWindowWrapper.getInstance(myProject).setSelectedNode(node);
}
}, true);
}
private static int getIndexToInsert(DefaultMutableTreeNode parent, DNamedElement namedElement) {
if (parent.getChildCount() == 0) return 0;
int res = 0;
for (DefaultMutableTreeNode child = (DefaultMutableTreeNode) parent.getFirstChild();
child != null;
child = (DefaultMutableTreeNode) parent.getChildAfter(child)) {
Object childObject = child.getUserObject();
if (!(childObject instanceof DNamedElement)) return 0;
String otherName = ((DNamedElement) childObject).getName();
if (otherName.compareTo(namedElement.getName()) > 0) return res;
res++;
}
return res;
}
@Override
public void addMethod(DynamicElementSettings settings) {
if (settings == null) return;
assert settings.isMethod();
final DMethodElement methodElement = (DMethodElement) createDynamicElement(settings);
final DClassElement classElement = getOrCreateClassElement(myProject, settings.getContainingClassName());
ToolWindow window = DynamicToolWindowWrapper.getInstance(myProject).getToolWindow(); //important to fetch myToolWindow before adding
classElement.addMethod(methodElement);
addItemInTree(classElement, methodElement, window);
}
@Override
public void removeClassElement(DClassElement classElement) {
final DRootElement rootElement = getRootElement();
rootElement.removeClassElement(classElement.getName());
removeClassFromTree(classElement);
}
private void removePropertyElement(DPropertyElement propertyElement) {
final DClassElement classElement = getClassElementByItem(propertyElement);
assert classElement != null;
classElement.removeProperty(propertyElement);
}
@Override
@NotNull
public Collection<DPropertyElement> findDynamicPropertiesOfClass(String className) {
final DClassElement classElement = findClassElement(getRootElement(), className);
if (classElement != null) {
return classElement.getProperties();
}
return new ArrayList<DPropertyElement>();
}
@Override
@Nullable
public String getPropertyType(String className, String propertyName) {
final DPropertyElement dynamicProperty = findConcreteDynamicProperty(getRootElement(), className, propertyName);
if (dynamicProperty == null) return null;
return dynamicProperty.getType();
}
@Override
@NotNull
public Collection<DClassElement> getAllContainingClasses() {
//TODO: use iterator
final DRootElement root = getRootElement();
return root.getContainingClasses();
}
@Override
public DRootElement getRootElement() {
return myRootElement;
}
@Override
@Nullable
public String replaceDynamicPropertyName(String className, String oldPropertyName, String newPropertyName) {
final DClassElement classElement = findClassElement(getRootElement(), className);
if (classElement == null) return null;
final DPropertyElement oldPropertyElement = classElement.getPropertyByName(oldPropertyName);
if (oldPropertyElement == null) return null;
classElement.removeProperty(oldPropertyElement);
classElement.addProperty(new DPropertyElement(oldPropertyElement.isStatic(), newPropertyName, oldPropertyElement.getType()));
fireChange();
DynamicToolWindowWrapper.getInstance(getProject()).rebuildTreePanel();
return newPropertyName;
}
@Override
@Nullable
public String replaceDynamicPropertyType(String className, String propertyName, String oldPropertyType, String newPropertyType) {
final DPropertyElement property = findConcreteDynamicProperty(className, propertyName);
if (property == null) return null;
property.setType(newPropertyType);
fireChange();
return newPropertyType;
}
/*
* Find dynamic property in class with name
*/
@Nullable
private static DMethodElement findConcreteDynamicMethod(DRootElement rootElement,
String containingClassName,
String methodName,
String[] parametersTypes) {
DClassElement classElement = findClassElement(rootElement, containingClassName);
if (classElement == null) return null;
return classElement.getMethod(methodName, parametersTypes);
}
// @Nullable
@Override
public DMethodElement findConcreteDynamicMethod(String containingClassName, String name, String[] parameterTypes) {
return findConcreteDynamicMethod(getRootElement(), containingClassName, name, parameterTypes);
}
private void removeMethodElement(DMethodElement methodElement) {
final DClassElement classElement = getClassElementByItem(methodElement);
assert classElement != null;
classElement.removeMethod(methodElement);
}
@Override
public void removeItemElement(DItemElement element) {
DClassElement classElement = getClassElementByItem(element);
if (classElement == null) return;
if (element instanceof DPropertyElement) {
removePropertyElement(((DPropertyElement) element));
} else if (element instanceof DMethodElement) {
removeMethodElement(((DMethodElement) element));
}
removeItemFromTree(element, classElement);
}
@Override
public void replaceDynamicMethodType(String className, String name, List<ParamInfo> myPairList, String oldType, String newType) {
final DMethodElement method = findConcreteDynamicMethod(className, name, QuickfixUtil.getArgumentsTypes(myPairList));
if (method == null) return;
method.setType(newType);
fireChange();
}
@Override
@NotNull
public DClassElement getOrCreateClassElement(Project project, String className) {
DClassElement classElement = DynamicManager.getInstance(myProject).getRootElement().getClassElement(className);
if (classElement == null) {
return new DClassElement(project, className);
}
return classElement;
}
@Override
@Nullable
public DClassElement getClassElementByItem(DItemElement itemElement) {
final Collection<DClassElement> classes = getAllContainingClasses();
for (DClassElement aClass : classes) {
if (aClass.containsElement(itemElement)) return aClass;
}
return null;
}
@Override
public void replaceDynamicMethodName(String className, String oldName, String newName, String[] types) {
final DMethodElement oldMethodElement = findConcreteDynamicMethod(className, oldName, types);
if (oldMethodElement != null) {
oldMethodElement.setName(newName);
}
DynamicToolWindowWrapper.getInstance(getProject()).rebuildTreePanel();
fireChange();
}
@Override
public Iterable<PsiMethod> getMethods(final String classQname) {
DClassElement classElement = getRootElement().getClassElement(classQname);
if (classElement == null) return Collections.emptyList();
return ContainerUtil.map(classElement.getMethods(), new Function<DMethodElement, PsiMethod>() {
@Override
public PsiMethod fun(DMethodElement methodElement) {
return methodElement.getPsi(PsiManager.getInstance(myProject), classQname);
}
});
}
@Override
public Iterable<PsiVariable> getProperties(final String classQname) {
DClassElement classElement = getRootElement().getClassElement(classQname);
if (classElement == null) return Collections.emptyList();
return ContainerUtil.map(classElement.getProperties(), new Function<DPropertyElement, PsiVariable>() {
@Override
public PsiVariable fun(DPropertyElement propertyElement) {
return propertyElement.getPsi(PsiManager.getInstance(myProject), classQname);
}
});
}
@Override
public void replaceClassName(final DClassElement oldClassElement, String newClassName) {
if (oldClassElement == null) return;
final DRootElement rootElement = getRootElement();
rootElement.removeClassElement(oldClassElement.getName());
oldClassElement.setName(newClassName);
rootElement.mergeAddClass(oldClassElement);
fireChange();
}
@Override
public void fireChange() {
fireChangeCodeAnalyze();
}
private void fireChangeCodeAnalyze() {
final Editor textEditor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
if (textEditor == null) return;
final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(textEditor.getDocument());
if (file == null) return;
((PsiModificationTrackerImpl)PsiManager.getInstance(myProject).getModificationTracker()).incCounter();
DaemonCodeAnalyzer.getInstance(myProject).restart();
}
@Override
@Nullable
public DPropertyElement findConcreteDynamicProperty(final String containingClassName, final String propertyName) {
return findConcreteDynamicProperty(getRootElement(), containingClassName, propertyName);
}
@Nullable
private static DPropertyElement findConcreteDynamicProperty(DRootElement rootElement, final String conatainingClassName, final String propertyName) {
final DClassElement classElement = rootElement.getClassElement(conatainingClassName);
if (classElement == null) return null;
return classElement.getPropertyByName(propertyName);
}
@Nullable
private static DClassElement findClassElement(DRootElement rootElement, final String conatainingClassName) {
return rootElement.getClassElement(conatainingClassName);
}
@Override
public void disposeComponent() {
}
@Override
@NotNull
public String getComponentName() {
return "DynamicManagerImpl";
}
@Override
public void projectOpened() {
}
@Override
public void projectClosed() {
}
/**
* On exit
*/
@Override
public DRootElement getState() {
// return XmlSerializer.serialize(myRootElement);
return myRootElement;
}
/*
* On loading
*/
@Override
public void loadState(DRootElement element) {
// myRootElement = XmlSerializer.deserialize(element, myRootElement.getClass());
myRootElement = element;
}
@Override
public DItemElement createDynamicElement(DynamicElementSettings settings) {
DItemElement itemElement;
if (settings.isMethod()) {
itemElement = new DMethodElement(settings.isStatic(), settings.getName(), settings.getType(), settings.getParams());
} else {
itemElement = new DPropertyElement(settings.isStatic(), settings.getName(), settings.getType());
}
return itemElement;
}
}