/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * DeprecationScanner.java * Created: Feb 13, 2007 * By: Joseph Wong */ package org.openquark.cal.compiler; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; /** * This class implements a compiler pass for finding all entities that are marked * as deprecated via a CALDoc deprecated block. * * @author Joseph Wong */ final class DeprecationScanner { /** * A {@link Comparator} for {@link org.openquark.cal.compiler.SourceModel.Name}s that * compares both the class and the source form of the names. */ private static final Comparator<SourceModel.Name> SOURCE_MODEL_NAME_COMPARATOR = new Comparator<SourceModel.Name>() { public int compare(final SourceModel.Name a, final SourceModel.Name b) { final int classNameResult = a.getClass().getName().compareTo(b.getClass().getName()); if (classNameResult != 0) { return classNameResult; } else { return a.toSourceText().compareTo(b.toSourceText()); } } }; /** * A set of {@link org.openquark.cal.compiler.SourceModel.Name}s for the deprecated entities collected so far. */ private final Set<SourceModel.Name> deprecatedEntityNames = new TreeSet<SourceModel.Name>(SOURCE_MODEL_NAME_COMPARATOR); /** * The {@link CALCompiler} instance encapsulating this instance. */ private final CALCompiler compiler; /** * This {@link SourceModelTraverser} implementation scans a CALDoc comment and makes a record of any deprecated blocks * that are found. * * @author Joseph Wong */ private final class DeprecationRecorder extends SourceModelTraverser<Void, Void> { /** * The name of the entity to be recorded as deprecated if a deprecated block is found. */ private final SourceModel.Name entityName; /** * Constructs an instance of this class. * @param entityName the name of the entity to be recorded as deprecated if a deprecated block is found. */ DeprecationRecorder(final SourceModel.Name entityName) { if (entityName == null) { throw new NullPointerException(); } this.entityName = entityName; } /** * {@inheritDoc} */ @Override public Void visit_CALDoc_TaggedBlock_Deprecated(SourceModel.CALDoc.TaggedBlock.Deprecated deprecatedBlock, Void arg) { deprecatedEntityNames.add(entityName); // no need to visit the subtree, so just return return null; } /** * {@inheritDoc} */ @Override public Void visit_CALDoc_TextBlock(SourceModel.CALDoc.TextBlock block, Void arg) { // Optimization - do not bother with text blocks and their contents return null; } } /** * This {@link SourceModelTraverser} implementation scans a module definition and processes its top level definitions * for potential deprecation. * * @author Joseph Wong */ private final class DeprecatedDefinitionsFinder extends SourceModelTraverser<Void, Void> { /** * The name of the module currently being processed. To be set via a call to {@link #visit_ModuleDefn}. */ private SourceModel.Name.Module currentModuleName; /** * {@inheritDoc} */ @Override public Void visit_FunctionDefn_Algebraic(final SourceModel.FunctionDefn.Algebraic defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.Function.make(currentModuleName, defn.getName())); // no need to visit the subtree, so just return return null; } /** * {@inheritDoc} */ @Override public Void visit_FunctionDefn_Foreign(final SourceModel.FunctionDefn.Foreign defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.Function.make(currentModuleName, defn.getName())); // no need to visit the subtree, so just return return null; } /** * {@inheritDoc} */ @Override public Void visit_FunctionDefn_Primitive(final SourceModel.FunctionDefn.Primitive defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.Function.make(currentModuleName, defn.getName())); // no need to visit the subtree, so just return return null; } /** * {@inheritDoc} */ @Override public Void visit_ModuleDefn(final SourceModel.ModuleDefn defn, Void arg) { currentModuleName = defn.getModuleName(); processCALDocComment(defn.getCALDocComment(), defn.getModuleName()); // call the superclass implementation to visit the subtree return super.visit_ModuleDefn(defn, arg); } /** * {@inheritDoc} */ @Override public Void visit_TypeClassDefn_ClassMethodDefn(final SourceModel.TypeClassDefn.ClassMethodDefn defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.Function.make(currentModuleName, defn.getMethodName())); // no need to visit the subtree, so just return return null; } /** * {@inheritDoc} */ @Override public Void visit_TypeClassDefn(final SourceModel.TypeClassDefn defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.TypeClass.make(currentModuleName, defn.getTypeClassName())); // call the superclass implementation to visit the subtree (for class methods) return super.visit_TypeClassDefn(defn, arg); } /** * {@inheritDoc} */ @Override public Void visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(final SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.DataCons.make(currentModuleName, defn.getDataConsName())); // no need to visit the subtree, so just return return null; } /** * {@inheritDoc} */ @Override public Void visit_TypeConstructorDefn_AlgebraicType(final SourceModel.TypeConstructorDefn.AlgebraicType defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.TypeCons.make(currentModuleName, defn.getTypeConsName())); // call the superclass implementation to visit the subtree (for data cons) return super.visit_TypeConstructorDefn_AlgebraicType(defn, arg); } /** * {@inheritDoc} */ @Override public Void visit_TypeConstructorDefn_ForeignType(final SourceModel.TypeConstructorDefn.ForeignType defn, final Void arg) { processCALDocComment(defn.getCALDocComment(), SourceModel.Name.TypeCons.make(currentModuleName, defn.getTypeConsName())); // no need to visit the subtree, so just return return null; } /** * Processes a CALDoc comment associated with the named entity by scanning it for deprecation blocks. * @param docComment the CALDoc comment to be processed. * @param entityName the name of the entity associated with the comment. */ private void processCALDocComment(final SourceModel.CALDoc docComment, final SourceModel.Name entityName) { if (docComment != null) { docComment.accept(new DeprecationRecorder(entityName), null); } } } /** * Constructs an instance of this class. * @param compiler the {@link CALCompiler} instance encapsulating this instance. */ DeprecationScanner(final CALCompiler compiler) { if (compiler == null) { throw new NullPointerException(); } this.compiler = compiler; } /** * Returns the type info for the named module. * @param moduleName a module name. * @return the corresponding type info. */ private ModuleTypeInfo getModuleTypeInfo(ModuleName moduleName) { return compiler.getPackager().getModuleTypeInfo(moduleName); } /** * Processes a module definition, scanning it for deprecated definitions and recording them. * @param moduleDefn the module definition. */ void processModule(final SourceModel.ModuleDefn moduleDefn) { moduleDefn.accept(new DeprecatedDefinitionsFinder(), null); } /** * Returns whether the named module is deprecated (either having been recorded as deprecated, or is deprecated * by virtue of having a deprecated block in the CALDoc comment of the associated {@link ModuleTypeInfo} object). * * @param moduleName a module name. * @return true if the named module is deprecated, false otherwise. */ boolean isModuleDeprecated(final ModuleName moduleName) { final boolean inSet = deprecatedEntityNames.contains(SourceModel.Name.Module.make(moduleName)); if (inSet) { return true; } final ModuleTypeInfo moduleTypeInfo = getModuleTypeInfo(moduleName); if (moduleTypeInfo != null) { return moduleTypeInfo.isDeprecated(); } return false; } /** * Returns whether the named type is deprecated (either having been recorded as deprecated, or is deprecated * by virtue of having a deprecated block in the CALDoc comment of the associated {@link TypeConstructor} object). * * @param typeName a type constructor name. * @return true if the named type is deprecated, false otherwise. */ boolean isTypeDeprecated(final QualifiedName typeName) { final boolean inSet = deprecatedEntityNames.contains(SourceModel.Name.TypeCons.make(typeName)); if (inSet) { return true; } final ModuleTypeInfo moduleTypeInfo = getModuleTypeInfo(typeName.getModuleName()); if (moduleTypeInfo != null) { final TypeConstructor typeCons = moduleTypeInfo.getTypeConstructor(typeName.getUnqualifiedName()); if (typeCons != null) { return typeCons.isDeprecated(); } } return false; } /** * Returns whether the named data constructor is deprecated (either having been recorded as deprecated, or is deprecated * by virtue of having a deprecated block in the CALDoc comment of the associated {@link DataConstructor} object). * * @param dataConsName a data constructor name. * @return true if the named data constructor is deprecated, false otherwise. */ boolean isDataConsDeprecated(final QualifiedName dataConsName) { final boolean inSet = deprecatedEntityNames.contains(SourceModel.Name.DataCons.make(dataConsName)); if (inSet) { return true; } final ModuleTypeInfo moduleTypeInfo = getModuleTypeInfo(dataConsName.getModuleName()); if (moduleTypeInfo != null) { final DataConstructor dataCons = moduleTypeInfo.getDataConstructor(dataConsName.getUnqualifiedName()); if (dataCons != null) { return dataCons.isDeprecated(); } } return false; } /** * Returns whether the named type class is deprecated (either having been recorded as deprecated, or is deprecated * by virtue of having a deprecated block in the CALDoc comment of the associated {@link TypeClass} object). * * @param typeClassName a type class name. * @return true if the named type class is deprecated, false otherwise. */ boolean isTypeClassDeprecated(final QualifiedName typeClassName) { final boolean inSet = deprecatedEntityNames.contains(SourceModel.Name.TypeClass.make(typeClassName)); if (inSet) { return true; } final ModuleTypeInfo moduleTypeInfo = getModuleTypeInfo(typeClassName.getModuleName()); if (moduleTypeInfo != null) { final TypeClass typeClass = moduleTypeInfo.getTypeClass(typeClassName.getUnqualifiedName()); if (typeClass != null) { return typeClass.isDeprecated(); } } return false; } /** * Returns whether the named function or class method is deprecated (either having been recorded as deprecated, or is deprecated * by virtue of having a deprecated block in the CALDoc comment of the associated {@link FunctionalAgent} object). * * @param functionOrClassMethodName the name of a function or class method. * @return true if the named function or class method is deprecated, false otherwise. */ boolean isFunctionOrClassMethodDeprecated(final QualifiedName functionOrClassMethodName) { final boolean inSet = deprecatedEntityNames.contains(SourceModel.Name.Function.make(functionOrClassMethodName)); if (inSet) { return true; } final ModuleTypeInfo moduleTypeInfo = getModuleTypeInfo(functionOrClassMethodName.getModuleName()); if (moduleTypeInfo != null) { final FunctionalAgent functionOrClassMethod = moduleTypeInfo.getFunctionOrClassMethod(functionOrClassMethodName.getUnqualifiedName()); if (functionOrClassMethod != null) { return functionOrClassMethod.isDeprecated(); } } return false; } }