001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.asJava; 018 019import com.intellij.openapi.diagnostic.Logger; 020import com.intellij.psi.*; 021import com.intellij.psi.impl.PsiModificationTrackerImpl; 022import com.intellij.psi.impl.PsiTreeChangeEventImpl; 023import com.intellij.psi.impl.PsiTreeChangePreprocessor; 024import com.intellij.psi.util.PsiModificationTracker; 025import org.jetbrains.jet.lang.psi.JetBlockExpression; 026import org.jetbrains.jet.lang.psi.JetClass; 027import org.jetbrains.jet.lang.psi.JetFile; 028 029public class JetCodeBlockModificationListener implements PsiTreeChangePreprocessor { 030 private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.asJava.JetCodeBlockModificationListener"); 031 032 private final PsiModificationTrackerImpl myModificationTracker; 033 034 public JetCodeBlockModificationListener(PsiModificationTracker modificationTracker) { 035 myModificationTracker = (PsiModificationTrackerImpl) modificationTracker; 036 } 037 038 @Override 039 public void treeChanged(PsiTreeChangeEventImpl event) { 040 if (!(event.getFile() instanceof JetFile)) return; 041 switch (event.getCode()) { 042 case BEFORE_CHILDREN_CHANGE: 043 case BEFORE_PROPERTY_CHANGE: 044 case BEFORE_CHILD_MOVEMENT: 045 case BEFORE_CHILD_REPLACEMENT: 046 case BEFORE_CHILD_ADDITION: 047 case BEFORE_CHILD_REMOVAL: 048 break; 049 050 case CHILD_ADDED: 051 case CHILD_REMOVED: 052 case CHILD_REPLACED: 053 processChange(event.getParent(), event.getOldChild(), event.getChild()); 054 break; 055 056 case CHILDREN_CHANGED: 057 // general childrenChanged() event after each change 058 if (!event.isGenericChildrenChange()) { 059 processChange(event.getParent(), event.getParent(), null); 060 } 061 break; 062 063 case CHILD_MOVED: 064 case PROPERTY_CHANGED: 065 myModificationTracker.incCounter(); 066 break; 067 068 default: 069 LOG.error("Unknown code:" + event.getCode()); 070 break; 071 } 072 } 073 074 private void processChange(PsiElement parent, PsiElement child1, PsiElement child2) { 075 try { 076 if (!isInsideCodeBlock(parent)) { 077 if (parent != null && parent.getContainingFile() instanceof JetFile) { 078 myModificationTracker.incCounter(); 079 } 080 else { 081 myModificationTracker.incOutOfCodeBlockModificationCounter(); 082 } 083 return; 084 } 085 086 if (containsClassesInside(child1) || child2 != child1 && containsClassesInside(child2)) { 087 myModificationTracker.incCounter(); 088 } 089 } catch (PsiInvalidElementAccessException e) { 090 myModificationTracker.incCounter(); // Shall not happen actually, just a pre-release paranoia 091 } 092 } 093 094 private static boolean containsClassesInside(PsiElement element) { 095 if (element == null) return false; 096 if (element instanceof PsiClass) return true; 097 098 PsiElement child = element.getFirstChild(); 099 while (child != null) { 100 if (containsClassesInside(child)) return true; 101 child = child.getNextSibling(); 102 } 103 104 return false; 105 } 106 107 private static boolean isInsideCodeBlock(PsiElement element) { 108 if (element instanceof PsiFileSystemItem) { 109 return false; 110 } 111 112 if (element == null || element.getParent() == null) return true; 113 114 PsiElement parent = element; 115 while (true) { 116 if (parent instanceof PsiFile || parent instanceof PsiDirectory || parent == null) { 117 return false; 118 } 119 if (parent instanceof JetClass) return false; // anonymous or local class 120 if (parent instanceof JetBlockExpression) { 121 return true; 122 } 123 parent = parent.getParent(); 124 } 125 } 126}