/* * Copyright 2014 Lukas Krejci * * 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.revapi.java.checks.generics; import java.util.ArrayList; import java.util.EnumSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import javax.lang.model.element.Parameterizable; import javax.lang.model.element.TypeParameterElement; import org.revapi.Difference; import org.revapi.java.spi.CheckBase; import org.revapi.java.spi.Code; import org.revapi.java.spi.JavaMethodElement; import org.revapi.java.spi.JavaModelElement; import org.revapi.java.spi.JavaTypeElement; import org.revapi.java.spi.Util; /** * @author Lukas Krejci * @since 0.1 */ public final class FormalTypeParametersChanged extends CheckBase { @Override public EnumSet<Type> getInterest() { return EnumSet.of(Type.CLASS, Type.METHOD); } @Override protected void doVisitClass(@Nullable JavaTypeElement oldType, @Nullable JavaTypeElement newType) { doVisit(oldType, newType); } @Override protected void doVisitMethod(@Nullable JavaMethodElement oldMethod, @Nullable JavaMethodElement newMethod) { doVisit(oldMethod, newMethod); } private void doVisit(@Nullable JavaModelElement oldElement, @Nullable JavaModelElement newElement) { if (oldElement == null || newElement == null || !isBothAccessible(oldElement, newElement)) { return; } Parameterizable oldEl = (Parameterizable) oldElement.getDeclaringElement(); Parameterizable newEl = (Parameterizable) newElement.getDeclaringElement(); List<? extends TypeParameterElement> oldPars = oldEl.getTypeParameters(); List<? extends TypeParameterElement> newPars = newEl.getTypeParameters(); if (oldPars.size() == 0 && oldPars.size() == newPars.size()) { return; } List<TypeParameterElement> added = new ArrayList<>(); List<TypeParameterElement> removed = new ArrayList<>(); Map<TypeParameterElement, TypeParameterElement> changed = new LinkedHashMap<>(); Iterator<? extends TypeParameterElement> oldIt = oldPars.iterator(); Iterator<? extends TypeParameterElement> newIt = newPars.iterator(); while (oldIt.hasNext() && newIt.hasNext()) { TypeParameterElement oldT = oldIt.next(); TypeParameterElement newT = newIt.next(); String oldS = Util.toUniqueString(oldT.asType()); String newS = Util.toUniqueString(newT.asType()); if (!oldS.equals(newS)) { changed.put(oldT, newT); } } while (oldIt.hasNext()) { removed.add(oldIt.next()); } while (newIt.hasNext()) { added.add(newIt.next()); } if (!added.isEmpty() || !removed.isEmpty() || !changed.isEmpty()) { pushActive(oldElement, newElement, added, removed, changed); } } @Nullable @Override protected List<Difference> doEnd() { ActiveElements<JavaModelElement> els = popIfActive(); if (els == null) { return null; } @SuppressWarnings("unchecked") List<TypeParameterElement> added = (List<TypeParameterElement>) els.context[0]; @SuppressWarnings("unchecked") List<TypeParameterElement> removed = (List<TypeParameterElement>) els.context[1]; @SuppressWarnings("unchecked") Map<TypeParameterElement, TypeParameterElement> changed = (Map<TypeParameterElement, TypeParameterElement>) els.context[2]; Parameterizable oldT = (Parameterizable) els.oldElement.getDeclaringElement(); List<Difference> diffs = new ArrayList<>(); if (oldT.getTypeParameters().isEmpty()) { diffs.add(createDifference(Code.GENERICS_ELEMENT_NOW_PARAMETERIZED, Code.attachmentsFor(els.oldElement, els.newElement))); } for (TypeParameterElement e : added) { diffs.add( createDifferenceWithExplicitParams(Code.GENERICS_FORMAL_TYPE_PARAMETER_ADDED, Code.attachmentsFor(els.oldElement, els.newElement), Util.toHumanReadableString(e)) ); } for (TypeParameterElement e : removed) { diffs.add(createDifferenceWithExplicitParams(Code.GENERICS_FORMAL_TYPE_PARAMETER_REMOVED, Code.attachmentsFor(els.oldElement, els.newElement), Util.toHumanReadableString(e))); } for (Map.Entry<TypeParameterElement, TypeParameterElement> e : changed.entrySet()) { String oldP = Util.toHumanReadableString(e.getKey()); String newP = Util.toHumanReadableString(e.getValue()); diffs.add(createDifferenceWithExplicitParams(Code.GENERICS_FORMAL_TYPE_PARAMETER_CHANGED, Code.attachmentsFor(els.oldElement, els.newElement, "oldTypeParameter", oldP, "newTypeParameter", newP), oldP, newP)); } return diffs; } }