Java tutorial
/* * Copyright 2000-2010 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 auto.fix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PackageScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.AllClassesSearch; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.occurences.ExpressionOccurenceManager; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; import com.intellij.util.Query; import org.jetbrains.annotations.NotNull; import java.util.*; /** * User: Call me Ismail * Date: Apr 28, 2010 * Time: 6:29:58 PM */ public class IntroduceAndPropagateConstantHandler extends BaseRefactoringProcessor implements PropagateSettingsIF { private Project myProject; private PsiLiteralExpression myLiteralExpression; private String lastActionCommand = IntroduceAndPropagateDialog.CLASS_ACTION_COMMAND; private static final String REFACTORING_NAME = "IntroduceAndPropagateConstant"; private String lastConstantName; private static final String CONSTANTS_IF_POSTFIX = "ConstantsIF"; private static final String JAVA_FILE_TYPE = ".java"; private static final boolean INCLUDE_SUBPACKAGES = true; public IntroduceAndPropagateConstantHandler(Project project, PsiLiteralExpression literalExpression) { super(project); myProject = project; myLiteralExpression = literalExpression; } public void invoke(Project project, PsiLiteralExpression psiExpression, ProblemDescriptor descriptor) { IntroduceAndPropagateDialog dialog = new IntroduceAndPropagateDialog(project, psiExpression, descriptor); dialog.addPropagateSettingsListener(this); dialog.setVisible(INCLUDE_SUBPACKAGES); } public void setPropagateSettings(String actionCommand, String constantName, boolean isPreview) { lastActionCommand = actionCommand; lastConstantName = constantName; setPreviewUsages(isPreview); super.doRun(); } private void processClassActionCommand(String constantName) { try { HashSet<PsiExpression> psiExpressions = findClassActionOccurrences( PsiUtil.getTopLevelClass(myLiteralExpression)); createConstants(psiExpressions, PsiUtil.getTopLevelClass(myLiteralExpression), PsiModifier.PRIVATE, false, constantName); } catch (PsiInvalidElementAccessException psi) { //ignore - this means that the occurrence of the expression no longer exists //that being the case, there's no need to carry out the refactoring } } private HashSet<PsiExpression> findClassActionOccurrences(PsiClass searchClass) { PsiExpression[] occurrences = new PsiExpression[0]; try { ExpressionOccurenceManager occurrenceManager = new ExpressionOccurenceManager(myLiteralExpression, searchClass, null); occurrences = occurrenceManager.getOccurences(); } catch (PsiInvalidElementAccessException psi) { //ignore - this means that the occurrence of the expression no longer exists } return /*cleanseFieldOccurrences*/(new HashSet<PsiExpression>(Arrays.asList(occurrences))); } private static HashSet<PsiExpression> cleanseFieldOccurrences(HashSet<PsiExpression> psiExpressions) { HashSet<PsiExpression> removeTheseExpression = new HashSet<PsiExpression>(); for (PsiExpression expression : psiExpressions) { if (PsiTreeUtil.getParentOfType(expression, PsiField.class) != null) { removeTheseExpression.add(expression); } } psiExpressions.removeAll(removeTheseExpression); return psiExpressions; } private void processPackageActionCommand(String constantName) { PsiPackage aPackage = retrievePackage(); HashSet<PsiExpression> elementsFound = findPackageActionOccurrences(aPackage); if (elementsFound.size() > 0) { PsiClass newInterface = createNewInterface(aPackage); createConstants(elementsFound, newInterface, PsiModifier.PACKAGE_LOCAL, INCLUDE_SUBPACKAGES, constantName); } } private HashSet<PsiExpression> findPackageActionOccurrences(PsiPackage aPackage) { HashSet<PsiExpression> elementsFound = new HashSet<PsiExpression>(); if (aPackage != null) { searchPackagesForOccurrences(aPackage, elementsFound); for (PsiPackage subPackage : aPackage.getSubPackages()) { searchPackagesForOccurrences(subPackage, elementsFound); } } return elementsFound; } private PsiPackage retrievePackage() { PsiPackage aPackage = null; try { String packageName = ((PsiJavaFile) myLiteralExpression.getContainingFile()).getPackageName(); aPackage = JavaPsiFacade.getInstance(myProject).findPackage(packageName); } catch (PsiInvalidElementAccessException psi) { // ignore - this is caused by auto mode processing elements that have already been replaced } return aPackage; } private void searchPackagesForOccurrences(PsiPackage aPackage, HashSet<PsiExpression> elementsFound) { if (aPackage != null) { GlobalSearchScope searchScope = PackageScope.packageScope(aPackage, INCLUDE_SUBPACKAGES); Query<PsiClass> query = AllClassesSearch.search(searchScope, myProject); getOccurrencesFromFindAllQuery(query, elementsFound); } } private static PsiClass createNewInterface(final PsiPackage aPackage) { final PsiClass[] targetClass = new PsiClass[1]; Runnable action = new Runnable() { public void run() { PsiDirectory directory = aPackage.getDirectories()[0]; String fileName = createConstantsIFName(aPackage.getName()) + CONSTANTS_IF_POSTFIX; String javaFileName = fileName + JAVA_FILE_TYPE; if (directory.findFile(javaFileName) == null) { targetClass[0] = JavaDirectoryService.getInstance().createInterface(directory, fileName); PsiUtil.setModifierProperty(targetClass[0], PsiKeyword.PUBLIC, INCLUDE_SUBPACKAGES); } else { for (PsiClass psiClass : JavaDirectoryService.getInstance().getClasses(directory)) { if (psiClass.getName().equals(fileName)) { targetClass[0] = psiClass; break; } } } } }; createAndRunCommand(aPackage.getProject(), action); return targetClass[0]; } private static void createAndRunCommand(Project project, final Runnable action) { CommandProcessor.getInstance().executeCommand(project, new Runnable() { public void run() { ApplicationManager.getApplication().runWriteAction(action); } }, REFACTORING_NAME, null); } private static String createConstantsIFName(String name) { String retVal = name; int lastIndexOf = name.lastIndexOf("."); if (lastIndexOf > 0) { retVal = name.substring(lastIndexOf + 1, name.length()); } String firstInitial = retVal.substring(0, 1); firstInitial = firstInitial.toUpperCase(); retVal = firstInitial + retVal.substring(1, retVal.length()); return retVal; } private void processClassHierarchyActionCommand(String constantName) { try { PsiClass topLevelClass = PsiUtil.getTopLevelClass(myLiteralExpression); PsiClass baseClass = findBaseClass(topLevelClass); HashSet<PsiExpression> tempElementsFound = findClassHierarchyActionOccurrences(baseClass); createConstants(tempElementsFound, baseClass, PsiModifier.PROTECTED, false, constantName); } catch (PsiInvalidElementAccessException psi) { // ignore - auto mode already replaced the literal expression } } private HashSet<PsiExpression> findClassHierarchyActionOccurrences(PsiClass baseClass) { @SuppressWarnings({ "ConstantConditions" }) SearchScope scope = GlobalSearchScope .moduleScope(ModuleUtil.findModuleForPsiElement(myLiteralExpression.getContainingFile())); Query<PsiClass> query = ClassInheritorsSearch.search(baseClass, scope, INCLUDE_SUBPACKAGES, INCLUDE_SUBPACKAGES); HashSet<PsiExpression> elementsFound = new HashSet<PsiExpression>(); getOccurrencesFromFindAllQuery(query, elementsFound); elementsFound.addAll(findClassActionOccurrences(baseClass)); return elementsFound; } private void getOccurrencesFromFindAllQuery(Query<PsiClass> query, HashSet<PsiExpression> elementsFound) { for (PsiClass psiClass : query.findAll()) { ExpressionOccurenceManager occurrenceManager = new ExpressionOccurenceManager(myLiteralExpression, psiClass, null); PsiExpression[] occurrences = occurrenceManager.getOccurences(); elementsFound.addAll(Arrays.asList(occurrences)); } } static PsiClass findBaseClass(PsiClass topLevelClass) { PsiClass retVal = topLevelClass; PsiReferenceList extendsList = topLevelClass.getExtendsList(); PsiClass superClass = topLevelClass.getSuperClass(); if (extendsList != null && extendsList.getChildren().length > 0 && superClass != null && superClass.isWritable()) { retVal = findBaseClass(superClass); } return retVal; } private static void createConstants(HashSet<PsiExpression> psiExpressions, PsiClass destinationClass, String visibility, boolean isDestinationClassNew, String constantName) { PsiManager psiManager = destinationClass.getManager(); PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory(); @SuppressWarnings({ "ConstantConditions" }) boolean fieldAlreadyExists = false; PsiField field = getExistingField(psiExpressions, destinationClass); psiExpressions = cleanseFieldOccurrences(psiExpressions); if (field == null) { //noinspection ConstantConditions field = factory.createField(checkNameAvailability(constantName, destinationClass), psiExpressions.iterator().next().getType()); field.setInitializer(psiExpressions.iterator().next()); //noinspection ConstantConditions field.getModifierList().setModifierProperty(visibility, true); /*if(anyStaticOccurrences(psiExpressions)) { //noinspection ConstantConditions field.getModifierList().setModifierProperty(PsiModifier.STATIC, true); } */ /*if(anyCaseStatementOccurrences(psiExpressions)) { */ //noinspection ConstantConditions field.getModifierList().setModifierProperty(PsiModifier.STATIC, true); //noinspection ConstantConditions field.getModifierList().setModifierProperty(PsiModifier.FINAL, true); /*}*/ field = (PsiField) CodeStyleManager.getInstance(psiManager.getProject()).reformat(field); } else { fieldAlreadyExists = true; } PsiElement multiExpressionsAnchor = RefactoringUtil.getAnchorElementForMultipleExpressions( psiExpressions.toArray(new PsiExpression[psiExpressions.size()]), null); if (multiExpressionsAnchor != null && destinationClass.equals(PsiUtil.getTopLevelClass(multiExpressionsAnchor))) { while (!multiExpressionsAnchor.getParent().equals(destinationClass)) { multiExpressionsAnchor = multiExpressionsAnchor.getParent(); } if (destinationClass.getFields().length > 0) { PsiField[] fields = destinationClass.getFields(); multiExpressionsAnchor = fields[fields.length - 1]; } } writeFieldToDestinationClass(destinationClass, isDestinationClassNew, field, fieldAlreadyExists, multiExpressionsAnchor, psiExpressions, createExpressionsClassMap(psiExpressions)); } private static boolean anyCaseStatementOccurrences(HashSet<PsiExpression> psiExpressions) { boolean retVal = false; for (PsiExpression expression : psiExpressions) { if (expression.getParent() instanceof PsiSwitchLabelStatement) { retVal = true; break; } } return retVal; } private static String checkNameAvailability(String constantName, PsiClass destinationClass) { String retVal = constantName; for (PsiField field : destinationClass.getAllFields()) { if (field.getName().equals(constantName)) { retVal += "_AUTO_CREATED"; break; } } return retVal; } private static PsiField getExistingField(HashSet<PsiExpression> psiExpressions, PsiClass destinationClass) { PsiField retVal = null; for (PsiExpression expression : psiExpressions) { if (expression.getType().equals(PsiType.BOOLEAN)) { break; } PsiField field = PsiTreeUtil.getParentOfType(expression, PsiField.class); if (field != null) { PsiClass topLevelClass = PsiUtil.getTopLevelClass(field); if (topLevelClass != null && topLevelClass.equals(destinationClass)) { retVal = field; psiExpressions.remove(expression); break; } } } return retVal; } private static HashMap<PsiExpression, PsiJavaFile> createExpressionsClassMap( HashSet<PsiExpression> psiExpressions) { HashMap<PsiExpression, PsiJavaFile> retVal = new HashMap<PsiExpression, PsiJavaFile>(); for (PsiExpression expression : psiExpressions) { retVal.put(expression, (PsiJavaFile) expression.getContainingFile()); } return retVal; } private static boolean anyStaticOccurrences(HashSet<PsiExpression> psiExpressions) { boolean retVal = false; Iterator<PsiExpression> iterator = psiExpressions.iterator(); while (iterator.hasNext() && !retVal) { PsiMethod method = PsiTreeUtil.getParentOfType(iterator.next(), PsiMethod.class); if (method != null) { retVal = method.hasModifierProperty(PsiModifier.STATIC) || method.isConstructor(); } } return retVal; } /*private static PsiMethod findContainingMethod(PsiExpression expression) { PsiElement methodElement= expression.getParent(); while(methodElement != null && !(methodElement instanceof PsiMethod)) { methodElement = methodElement.getParent(); } assert methodElement != null; return (PsiMethod)methodElement; }*/ private static void writeFieldToDestinationClass(final PsiClass destinationClass, final boolean isDestinationClassNew, final PsiField field, final boolean fieldAlreadyExists, final PsiElement multiExpressionsAnchor, final HashSet<PsiExpression> psiExpressions, final HashMap<PsiExpression, PsiJavaFile> expressionsClassMap) { final PsiJavaFile destinationFile = (PsiJavaFile) destinationClass.getContainingFile(); Runnable action = new Runnable() { public void run() { Project project = field.getManager().getProject(); if (!fieldAlreadyExists) { destinationClass.add(field); if (multiExpressionsAnchor != null && multiExpressionsAnchor instanceof PsiField) { destinationClass.addAfter(CodeEditUtil.createLineFeed(field.getManager()), multiExpressionsAnchor); CodeStyleManager.getInstance(project).adjustLineIndent(destinationClass.getContainingFile(), field.getTextRange()); } } for (PsiExpression occurrence : psiExpressions) { JavaPsiFacade javaFacade = JavaPsiFacade.getInstance(project); PsiElementFactory factory = javaFacade.getElementFactory(); IntroduceVariableBase.replace(occurrence, factory.createExpressionFromText( isDestinationClassNew ? destinationClass.getName() + "." + field.getName() : field.getName(), field.getContext()), project); PsiJavaFile occurrenceJavaFile = expressionsClassMap.get(occurrence); if (!destinationFile.getPackageName().equals(occurrenceJavaFile.getPackageName())) { //noinspection ConstantConditions occurrenceJavaFile.getImportList().add(JavaPsiFacade.getInstance(project) .getElementFactory().createImportStatement(destinationClass)); } } } }; createAndRunCommand(field.getProject(), action); } static String extractDefaultFieldName(PsiExpression psiExpression) { String retVal = "DEFAULT_FIELD_NAME"; PsiMethod psiMethod = null; psiMethod = resolveMethod(psiExpression, psiMethod); if (psiMethod != null && psiExpression.getParent() instanceof PsiExpressionList) { retVal = getParameterName(psiExpression, retVal, psiMethod); String text = psiExpression.getText(); if (text.matches(".http.*")) { int lastIndex = text.lastIndexOf("/"); text = text.substring(lastIndex + 1, text.length()); } retVal += "_" + text; retVal = cleanSpecialCharacters(retVal); } else if (psiExpression != null) { retVal = getQuickAndDirtyName(psiExpression, retVal); } return retVal; } private static PsiMethod resolveMethod(PsiExpression psiExpression, PsiMethod psiMethod) { PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(psiExpression, PsiNewExpression.class); if (newExpression != null) { psiMethod = newExpression.resolveConstructor(); } else { PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(psiExpression, PsiMethodCallExpression.class); if (methodCallExpression != null) { psiMethod = (PsiMethod) methodCallExpression.getMethodExpression().resolve(); } } return psiMethod; } private static String getQuickAndDirtyName(PsiExpression psiExpression, String retVal) { try { PsiType type = psiExpression.getType(); if (type != PsiType.DOUBLE && type != PsiType.BYTE && type != PsiType.FLOAT && type != PsiType.INT && type != PsiType.LONG && type != PsiType.SHORT) { retVal = psiExpression.getText(); retVal = cleanSpecialCharacters(retVal); if (retVal.length() == 0) { retVal = psiExpression.getText(); retVal = retVal.replaceAll("[\"]", ""); retVal = retVal.replaceAll("[\\\\]", "SLASH"); retVal = retVal.replaceAll("[=]", "EQUALS"); retVal = retVal.replaceAll("[*]", "ASTERISK"); retVal = retVal.replaceAll("[(]", "OPEN_PARENTHESIS"); retVal = retVal.replaceAll("[)]", "CLOSE_PARENTHESIS"); } } else { //noinspection ConstantConditions retVal = psiExpression.getType().getPresentableText().toUpperCase() + "_" + psiExpression.getText(); } } catch (NullPointerException np) { //ignore - this is a result of the expression no longer having a reference to the file //this is caught again later on and results in no refactoring being performed } return retVal; } private static String cleanSpecialCharacters(String retVal) { retVal = retVal.replaceAll(";", "SEMICOLON"); retVal = retVal.replaceAll(",", "COMMA"); retVal = retVal.replaceAll("[\']", "SINGLE_QUOTE"); retVal = retVal.replaceAll("[\\\\]", ""); retVal = retVal.replaceAll("[\\\"=*():#$@!^]", ""); retVal = retVal.replaceAll("[%]", "_PERCENT"); retVal = retVal.replaceAll(" ", "_"); retVal = retVal.replaceAll("[.]", "_"); retVal = retVal.replaceAll("-", "_"); retVal = retVal.replaceAll("/", "_"); if (retVal.startsWith("_")) { retVal = retVal.substring(1); } if (Character.isDigit(retVal.charAt(0))) { retVal = "CONST_" + retVal; } retVal = retVal.trim(); retVal = retVal.toUpperCase(); return retVal; } private static String getParameterName(PsiExpression psiExpression, String retVal, PsiMethod psiMethod) { final PsiElement navElement = psiMethod.getNavigationElement(); if (navElement instanceof PsiMethod) { psiMethod = (PsiMethod) navElement; } PsiExpression[] expressions = ((PsiExpressionList) psiExpression.getParent()).getExpressions(); int parameterListLocation = -1; for (int i = 0; i < expressions.length; i++) { if (expressions[i] == psiExpression) { parameterListLocation = i; break; } } PsiParameter[] parms = psiMethod.getParameterList().getParameters(); if (parameterListLocation < parms.length) { retVal = parms[parameterListLocation].getName(); /*if (identifier != null) { String name = identifier.getText(); if (name != null) { *//*name = variableNameToPropertyName(name, VariableKind.PARAMETER); String[] names = getSuggestionsByName(name, variableKind, false); return new NamesByExprInfo(name, names);*//* retVal = name.toUpperCase(); } }*/ } return retVal.toUpperCase(); } @Override protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { HashSet<PsiExpression> hashSet = findOccurrences(); return new IntroduceAndPropagateConstantViewDescriptor(hashSet); } private HashSet<PsiExpression> findOccurrences() { HashSet<PsiExpression> hashSet = new HashSet<PsiExpression>(); try { hashSet = findClassActionOccurrences(PsiUtil.getTopLevelClass(myLiteralExpression)); if (lastActionCommand.equals(IntroduceAndPropagateDialog.CLASS_HIERARCHY_COMMAND)) { hashSet = findClassHierarchyActionOccurrences( findBaseClass(PsiUtil.getTopLevelClass(myLiteralExpression))); } else if (lastActionCommand.equals(IntroduceAndPropagateDialog.PACKAGE_ACTION_COMMAND)) { hashSet = findPackageActionOccurrences(retrievePackage()); } } catch (PsiInvalidElementAccessException psi) { // ignore - auto mode already replaced the element } return hashSet; } @NotNull @Override protected UsageInfo[] findUsages() { HashSet<PsiExpression> occurrences; occurrences = findOccurrences(); UsageInfo[] retVal = new UsageInfo[occurrences.size()]; ArrayList<UsageInfo> temp = new ArrayList<UsageInfo>(); for (PsiExpression expression : occurrences) { temp.add(new UsageInfo(expression)); } retVal = temp.toArray(retVal); return retVal; } @Override protected void refreshElements(PsiElement[] elements) { //TODO: what is this? //To change body of implemented methods use File | Settings | File Templates. } @Override protected void performRefactoring(UsageInfo[] usages) { if (lastActionCommand.equals(IntroduceAndPropagateDialog.CLASS_HIERARCHY_COMMAND)) { processClassHierarchyActionCommand(lastConstantName); } else if (lastActionCommand.equals(IntroduceAndPropagateDialog.CLASS_ACTION_COMMAND)) { processClassActionCommand(lastConstantName); } else if (lastActionCommand.equals(IntroduceAndPropagateDialog.PACKAGE_ACTION_COMMAND)) { processPackageActionCommand(lastConstantName); } else { Messages.showErrorDialog("Action setting " + lastActionCommand + " not found", "Missing action command"); } } @Override protected String getCommandName() { return REFACTORING_NAME; } }