Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.SourceMetricsManager$ResultsCollector

/*
* 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.
*/


/*
* WorkspaceSourceMetricsManager.java
* Creation date: (Apr 14, 2005)
* By: Jawright
*/
package org.openquark.cal.compiler;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.openquark.cal.compiler.ClassInstanceIdentifier.UniversalRecordInstance;
import org.openquark.cal.compiler.SearchResult.Precise;
import org.openquark.cal.compiler.SearchResult.Frequency.Type;
import org.openquark.cal.compiler.SourceIdentifier.Category;
import org.openquark.cal.compiler.SourceMetricFinder.LintWarning;
import org.openquark.cal.compiler.SourceMetricFinder.SearchType;
import org.openquark.cal.compiler.SourceModel.FunctionTypeDeclaration;
import org.openquark.cal.compiler.SourceModel.InstanceDefn;
import org.openquark.cal.compiler.SourceModel.SourceElement;
import org.openquark.cal.compiler.SourceModel.TopLevelSourceElement;
import org.openquark.cal.compiler.SourceModel.InstanceDefn.InstanceTypeCons;
import org.openquark.cal.compiler.SourceModel.InstanceDefn.InstanceTypeCons.TypeCons;
import org.openquark.cal.compiler.SourceModel.TypeClassDefn.ClassMethodDefn;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn;
import org.openquark.cal.filter.AcceptAllModulesFilter;
import org.openquark.cal.filter.AcceptAllQualifiedNamesFilter;
import org.openquark.cal.filter.ModuleFilter;
import org.openquark.cal.filter.QualifiedNameFilter;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.util.Pair;
import org.openquark.util.WildcardPatternMatcher;


/**
* This internal class encapsulates aggregated source metrics for a set of modules.
* It should be instantiated only by the CALWorkspace class.  Other clients should use
* the WorkspaceSourceMetrics interface instead.
*
* This class aggregates the module-level source metric information held in modules'
* ModuleTypeInfo objects.
*
* It is not safe to concurrently modify this class.
* Creation date: (Apr 14, 2005)
* @author Jawright
*/
public final class SourceMetricsManager implements SourceMetrics {

    /** Map from gem name to number of times the gem is referred
     * to in the function bodies of functions defined in modules contained by the containing
     * workspace.
     */
    private final Map<QualifiedName, Integer> gemToReferenceFrequencyMap = new HashMap<QualifiedName, Integer>();
   
    /** The containing workspace. */
    private final ModuleContainer moduleContainer;

    /**
     * Used to provide feedback on the progress of the search.
     *
     * @author GMcClement
     *
     */
    public interface IProgressMonitor {
        /**
         * Called at the start of the search to provide the number of modules to be searched.
         * @param numberOfModules The number of modules that are going to be searched.
         */
        public void numberOfModules(int numberOfModules);
        /**
         * Called just before the given module is searched.
         * @param module The name of the next module to be searched.
         */
        public void startModule(ModuleName module);
       
        /**
         * Called when the search is completed.
         */
        public void done();
       
        /**
         * Check the current activity was cancelled.
         * @return the flag indicating if the activity should be cancelled.
         */
        public boolean isCancelled();

        /**
         * Called when more results from the search are available.
         * @param moreResults The additional search results that have been found.
         */
        public void moreResults(Collection<? extends SearchResult> moreResults);
    }
   
    /** Not intended to be used outside of the CALWorkspace class */
    public SourceMetricsManager(ModuleContainer moduleContainer) {
        this.moduleContainer = moduleContainer;
    }

    /**
     * Add in the metrics for the specified module to the aggregate collection.
     * This method is only intended to be called from CALWorkspace.
     * @param moduleTypeInfo ModuleTypeInfo of module to add to the metrics
     */
    public synchronized void addModuleMetrics(ModuleTypeInfo moduleTypeInfo) {

        // If there are no metrics being kept, then there's nothing for us to do
        if (moduleTypeInfo.getModuleSourceMetrics() == null) {
            return;
        }
       
        Map<QualifiedName, Integer> updateMap = moduleTypeInfo.getModuleSourceMetrics().getGemToLocalReferenceFrequencyMap();
       
        // Add reference-frequency metrics
        addFrequencyMetrics(updateMap, gemToReferenceFrequencyMap);
    }
   
    /**
     * Remove the metrics for the specified module from the aggregate collection.
     * This method is only intended to be called from CALWorkspace.
     * @param moduleTypeInfo ModuleTypeInfo of module to add to the metrics
     */
    public synchronized void removeModuleMetrics(ModuleTypeInfo moduleTypeInfo) {

        // If there are no metrics being kept, then there's nothing for us to do
        if (moduleTypeInfo.getModuleSourceMetrics() == null) {
            return;
        }
       
        Map<QualifiedName, Integer> updateMap = moduleTypeInfo.getModuleSourceMetrics().getGemToLocalReferenceFrequencyMap();

        // Remove reference-frequency metrics
        removeFrequencyMetrics(updateMap, gemToReferenceFrequencyMap);
    }

    /**
     * Aggregate the frequency data in sourceMap into destinationMap.
     * If (K,V) is in sourceMap but no mapping for K exists in destinationMap, then
     * (K,V) is added to destinationMap.  If (K,V1) is in sourceMap and (K,V2) is
     * in destinationMap, then destinationMap is updated to contain (K, V1+V2). 
     * @param sourceMap Map to add to destinationMap
     * @param destinationMap Map to update
     */
    private static <T> void addFrequencyMetrics(Map<T, Integer> sourceMap, Map<T, Integer> destinationMap) {
        for (final Map.Entry<T, Integer> entry : sourceMap.entrySet()) {
            Integer newValue = entry.getValue();
            addFrequencyEntry(entry.getKey(), newValue.intValue(), destinationMap);
        }
    }
   
    /**
     * Adds (key, frequency) to destinationMap if destinationMap does not already contain
     * a value for key.  If destinationMap does contain a value for key, then it is replaced
     * by (key, frequency + origValue).
     * @param key Key to update entry for
     * @param frequency Value to add to the entry for key
     * @param destinationMap Map to add the entry to
     */
    private static <T> void addFrequencyEntry(T key, int frequency, Map<T, Integer> destinationMap) {
        Integer currentValue = destinationMap.get(key);
       
        if (currentValue != null) {
            destinationMap.put(key, Integer.valueOf(currentValue.intValue() + frequency));
        } else {
            destinationMap.put(key, Integer.valueOf(frequency));
        }
    }
   
    /**
     * Remove the frequency data in removalItems from the aggregate totals
     * in targetMap. 
     * @param removalItems Map of values to remove from the totals in targetMap
     * @param targetMap Map to update
     */
    private static <T> void removeFrequencyMetrics(Map<T, Integer> removalItems, Map<T, Integer> targetMap) {
        for (final Map.Entry<T, Integer> entry : removalItems.entrySet()) {
            T key = entry.getKey();
            Integer removalValue = entry.getValue();
            Integer currentValue = targetMap.get(key);
           
            if (currentValue != null) {
                targetMap.put(key, Integer.valueOf(currentValue.intValue() - removalValue.intValue()));
                if (currentValue.intValue() - removalValue.intValue() < 0) {
                    throw new IllegalStateException("Module-removal operation on WorkspaceSourceMetricsManager caused a negative frequency for "+key);
                }
            } else {
                throw new IllegalStateException("Module-removal operation on WorkspaceSourceMetricsManager caused an attempt to remove missing key "+key+" from WorkspaceSourceMetricsManager tracking");
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public synchronized int getGemReferenceFrequency(QualifiedName gemName) {
        Integer result = gemToReferenceFrequencyMap.get(gemName);
        if (result != null) {
            return result.intValue();
        } else {
            return 0;
        }
    }
   
    /**
     * {@inheritDoc}
     */
    public String dumpCompositionalFrequencies(ModuleFilter moduleFilter, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions) {
        StringBuilder buffer = new StringBuilder();
        Map<Pair<QualifiedName, QualifiedName>, Integer> workspaceCompositionalFrequencies = new HashMap<Pair<QualifiedName, QualifiedName>, Integer>();
           
        for(int i = 0; i < moduleContainer.getNModules(); i++) {
            ModuleTypeInfo moduleTypeInfo = moduleContainer.getNthModuleTypeInfo(i);

            if(moduleFilter.acceptModule(moduleTypeInfo.getModuleName())) {

                // Parse a SourceModel to process
                CompilerMessageLogger logger = new MessageLogger();
                SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleTypeInfo.getModuleName(), false, logger);
   
                // Fold this module's frequencies into the grand total
                Map<Pair<QualifiedName, QualifiedName>, Integer> compositionalFrequencies = SourceMetricFinder.computeCompositionalFrequencies(moduleDefn, moduleTypeInfo, functionFilter, traceSkippedFunctions);
                addFrequencyMetrics(compositionalFrequencies, workspaceCompositionalFrequencies);
            } else {
                System.out.println("Skipping test module " + moduleTypeInfo.getModuleName());
            }
        }
           
        // Build a string from the grand total
        for (final Map.Entry<Pair<QualifiedName, QualifiedName>, Integer> entry : workspaceCompositionalFrequencies.entrySet()) {
            Pair<QualifiedName, QualifiedName> key = entry.getKey();
            Integer frequency = entry.getValue();
           
            buffer.append(key.fst());
            buffer.append(",");
            buffer.append(key.snd());
            buffer.append(",");
            buffer.append(frequency.intValue());
            buffer.append("\n");
        }

        return buffer.toString();
    }
   
    /**
     * {@inheritDoc}
     */
    public synchronized String dumpReferenceFrequencies(ModuleFilter moduleFilter, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions) {
        StringBuilder buffer = new StringBuilder();
        Set<Map.Entry<QualifiedName, Integer>> dataEntries = null;
       
        if (moduleFilter instanceof AcceptAllModulesFilter && functionFilter instanceof AcceptAllQualifiedNamesFilter) {
            // If there are no filtering options selected, then we can just dump the cache
            dataEntries = gemToReferenceFrequencyMap.entrySet();
           
        } else {
            Map<Pair<QualifiedName, QualifiedName>, Integer> workspaceDependeeFrequencies = new HashMap<Pair<QualifiedName, QualifiedName>, Integer>();
            Map<QualifiedName, Integer> workspaceReferenceFrequencies = new HashMap<QualifiedName, Integer>();
           
            for(int i = 0; i < moduleContainer.getNModules(); i++) {
                ModuleTypeInfo moduleTypeInfo = moduleContainer.getNthModuleTypeInfo(i);
               
                if(moduleFilter.acceptModule(moduleTypeInfo.getModuleName())) {
   
                    // Parse a SourceModel to process
                    SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleTypeInfo.getModuleName(), false, new MessageLogger());
       
                    // Fold this module's frequencies into the grand total
                    Map<Pair<QualifiedName, QualifiedName>, Integer> referenceFrequencies = SourceMetricFinder.computeReferenceFrequencies(moduleDefn, moduleTypeInfo, functionFilter, traceSkippedFunctions);
                    addFrequencyMetrics(referenceFrequencies, workspaceDependeeFrequencies);
                   
                } else {
                    System.out.println("Skipping test module " + moduleTypeInfo.getModuleName());
                }
            }

            // Summarize the dependee data into reference frequency data
            for (final Map.Entry<Pair<QualifiedName, QualifiedName>, Integer> entry : workspaceDependeeFrequencies.entrySet()) {
                Pair<QualifiedName, QualifiedName> dependeePair = entry.getKey();
                Integer frequency = entry.getValue();
                QualifiedName dependee = dependeePair.fst();
               
                addFrequencyEntry(dependee, frequency.intValue(), workspaceReferenceFrequencies);
            }

            dataEntries = workspaceReferenceFrequencies.entrySet();
        }
               
        // Iterate over the possibly-filtered collection and dump them out
        for (final Map.Entry<QualifiedName, Integer> entry : dataEntries) {
            QualifiedName name = entry.getKey();
            Integer frequency = entry.getValue();
           
            buffer.append(name.getModuleName());
            buffer.append(",");
            buffer.append(name.getUnqualifiedName());
            buffer.append(",");
            buffer.append(frequency.intValue());
            buffer.append("\n");
        }
       
        return buffer.toString();
    }
   
    /**
     * {@inheritDoc}
     */
    public void dumpLintWarnings(ModuleFilter moduleFilter, QualifiedNameFilter functionFilter, boolean traceSkippedModulesAndFunctions,
                                 boolean includeUnplingedPrimitiveArgs, boolean includeRedundantLambdas, boolean includeUnusedPrivates, boolean includeMismatchedWrapperPlings, boolean includeUnreferencedLetVariables) {
       
       
        for(int i = 0; i < moduleContainer.getNModules(); i++) {
            ModuleTypeInfo moduleTypeInfo = moduleContainer.getNthModuleTypeInfo(i);

            if(moduleFilter.acceptModule(moduleTypeInfo.getModuleName())) {

                // Parse a SourceModel to process
                SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleTypeInfo.getModuleName(), false,  new MessageLogger());
               
                // Dump the warnings
                List<LintWarning> warnings = SourceMetricFinder.computeLintWarnings(moduleDefn, moduleTypeInfo, functionFilter, traceSkippedModulesAndFunctions,
                                                                       includeUnplingedPrimitiveArgs, includeRedundantLambdas, includeUnusedPrivates, includeMismatchedWrapperPlings, includeUnreferencedLetVariables);
                for (final LintWarning lintWarning : warnings) {
                    System.out.println(lintWarning.toString());
                }
            } else if(traceSkippedModulesAndFunctions) {
                System.out.println("Skipping test module " + moduleTypeInfo.getModuleName());
            }
        }
    }

    /**
     * Helper method for all the different kinds of search.  Walks over each
     * module in the workspace, performing the specified SourceMetricFinder search
     * on each module where the target entity is visible.
     * @param targetEntityList List of ScopedEntities to search for
     * @param searchType Type of search to perform
     * @return List of SearchResults of search hits
     */
    private List<SearchResult> performSearch(List<? extends ScopedEntity> targetEntityList, SourceMetricFinder.SearchType searchType, CompilerMessageLogger messageLogger) {
       
        if(targetEntityList.isEmpty()) {
            return Collections.emptyList();
        }
       
        List<SearchResult> searchResults = new ArrayList<SearchResult>();

        // Process modules in sorted order to ensure that the results come
        // back sorted by module name.
        for (final ModuleTypeInfo moduleTypeInfo : getSortedModuleTypeInfos()) {
            Set<QualifiedName> qualifiedTargets = new HashSet<QualifiedName>();
           
            for (final ScopedEntity scopedEntity : targetEntityList) {
                if(isEntityVisibleToModule(scopedEntity, moduleTypeInfo)) {
                    qualifiedTargets.add(scopedEntity.getName());
                }
            }
           
            if(qualifiedTargets.size() == 0) {
                continue;
            }
           
            List<QualifiedName> targetNames = new ArrayList<QualifiedName>(qualifiedTargets);
           
            if(!moduleContainsHits(moduleTypeInfo, targetNames, searchType)) {
                continue;
            }
           
            // Parse a SourceModel to process
            MessageLogger moduleLogger = new MessageLogger();
            ModuleName moduleName = moduleTypeInfo.getModuleName();
           
            SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, false, moduleLogger);
            // if the module is a sourceless module, then we need to process it differently.
            if (moduleDefn == null && moduleLogger.getNErrors() == 0) {
                searchResults.addAll(performSourcelessSearch(moduleTypeInfo, targetNames, searchType));
                continue;
            }
           
            if(moduleDefn == null) {
                // Copy messages, adding source names to each SourcePosition
                for (final CompilerMessage compilerMessage : moduleLogger.getCompilerMessages()) {
                    SourceRange sourceRange = compilerMessage.getSourceRange();
                    if(sourceRange != null) {
                        SourceRange augmentedRange = getAugmentedRange(sourceRange, moduleName);
                        messageLogger.logMessage(new CompilerMessage(augmentedRange, compilerMessage.getMessageKind()));
                    } else {
                        messageLogger.logMessage(compilerMessage);
                    }
                }
                continue;
            }
           
            // Find all the hits in the module and absorb them into the full total
            List<SearchResult.Precise> moduleSearchResults = SourceMetricFinder.performSearch(moduleDefn, moduleTypeInfo, searchType, targetNames);
            searchResults.addAll(augmentSearchResultsWithContextLines(moduleSearchResults, moduleContainer.getModuleSource(moduleName)));
        }
   
        return Collections.unmodifiableList(searchResults);
    }

    /**
     * Return a source range the same as the given one except that the module name is updated.
     * @param sourceRange the source range to add the module name to
     * @param moduleName the module name to add to the source range
     * @return a new source range that sames as the given one except that the module name is updated.
     */
    private SourceRange getAugmentedRange(SourceRange sourceRange, ModuleName moduleName){
        SourcePosition augmentedStartPosition = new SourcePosition(sourceRange.getStartLine(), sourceRange.getStartColumn(), moduleName);
        SourcePosition augmentedEndPosition = new SourcePosition(sourceRange.getEndLine(), sourceRange.getEndColumn(), moduleName);
        return new SourceRange(augmentedStartPosition, augmentedEndPosition);
    }

    /**
     * Helper method for all the different kinds of search. Walks over each
     * module in the workspace, performing the specified SourceMetricFinder
     * search on each module where the target entity is visible.
     *
     * @param targetEntityList
     *            List of ScopedEntities to search for
     * @param targetModuleTypeInfos The list of moduleTypeInfos to search in the order to be searched.
     * @param searchType Type of search to perform
     * @param monitor A callback function to receive information about the progression of the search. This may be null.
     */
    private void performSearch(
            List<? extends ScopedEntity> targetEntityList,
            Collection<ModuleTypeInfo> targetModuleTypeInfos,
            SourceMetricFinder.SearchType searchType,
            IProgressMonitor monitor,
            CompilerMessageLogger messageLogger) {

        if (targetEntityList.isEmpty()) {
            return;
        }

        if (monitor != null){
            monitor.numberOfModules(targetModuleTypeInfos.size());
        }
       
        // Process modules in sorted order to ensure that the results come
        // back sorted by module name.
        for (final ModuleTypeInfo moduleTypeInfo : targetModuleTypeInfos) {
            if (monitor != null){
                monitor.startModule(moduleTypeInfo.getModuleName());
                if (monitor.isCancelled()){
                    return;
                }
            }           
           
            Set<QualifiedName> qualifiedTargets = new HashSet<QualifiedName>();

            for (final ScopedEntity scopedEntity : targetEntityList) {
                if (isEntityVisibleToModule(scopedEntity, moduleTypeInfo)) {
                    qualifiedTargets.add(scopedEntity.getName());
                }
            }

            if (qualifiedTargets.size() == 0) {
                continue;
            }

            List<QualifiedName> targetNames = new ArrayList<QualifiedName>(qualifiedTargets);

            if (!moduleContainsHits(moduleTypeInfo, targetNames,
                    searchType)) {
                continue;
            }

            // Parse a SourceModel to process
            MessageLogger moduleLogger = new MessageLogger();
            ModuleName moduleName = moduleTypeInfo.getModuleName();

            SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, false, moduleLogger);
            // if the module is a sourceless module, then we need to process it
            // differently.
            if (moduleDefn == null && moduleLogger.getNErrors() == 0) {
                monitor.moreResults(performSourcelessSearch(moduleTypeInfo, targetNames, searchType));
                continue;
            }


            if (moduleDefn == null) {
                // Copy messages, adding source names to each SourcePosition
                for (final CompilerMessage compilerMessage : moduleLogger.getCompilerMessages()) {
                    final SourceRange sourceRange = compilerMessage.getSourceRange();
                    if (sourceRange != null) {
                        SourceRange augmentedSourceRange = getAugmentedRange(sourceRange, moduleName);
                        messageLogger.logMessage(new CompilerMessage(
                            augmentedSourceRange, compilerMessage.getMessageKind()));
                    } else {
                        messageLogger.logMessage(compilerMessage);
                    }
                }
                continue;
            }

            // Find all the hits in the module and absorb them into the full
            // total
            List<SearchResult.Precise> moduleSearchResults = SourceMetricFinder.performSearch(
                    moduleDefn, moduleTypeInfo, searchType, targetNames);
            Collection<SearchResult> moreResults =
                augmentSearchResultsWithContextLines(moduleSearchResults, moduleContainer.getModuleSource(moduleName));
            monitor.moreResults(moreResults);
        }
    }
   
    /**
     * Searches a sourceless module for references, definitions, or instances, and returns a list of SearchResult
     * representing the hits.
     * <p>
     * NOTE: this method is closely related to {@link #moduleContainsHits}. Modifying one should require modifications in the other.
     *
     * @param moduleTypeInfo the ModuleTypeInfo for the sourceless module to be searched.
     * @param targetNames the List of QualifiedNames of the entities to search for.
     * @param searchType type of search to perform.
     * @return a List of the search results.
     */
    private List<SearchResult.Frequency> performSourcelessSearch(ModuleTypeInfo moduleTypeInfo, List<QualifiedName> targetNames, SourceMetricFinder.SearchType searchType) {
       
        ModuleName moduleName = moduleTypeInfo.getModuleName();
        LinkedHashMap<Pair<QualifiedName, SearchResult.Frequency.Type>, Integer> results = new LinkedHashMap<Pair<QualifiedName, SearchResult.Frequency.Type>, Integer>();

        if(searchType == SourceMetricFinder.SearchType.REFERENCES || searchType == SourceMetricFinder.SearchType.REFERENCES_CONSTRUCTIONS || searchType == SourceMetricFinder.SearchType.ALL) {
           
            for(int i = 0; i < moduleTypeInfo.getNFunctions(); i++) {
                Function functionEntity = moduleTypeInfo.getNthFunction(i);
                for (final QualifiedName qualifiedName : targetNames) {
                    Map<QualifiedName, Integer> dependeeToFrequencyMap = functionEntity.getDependeeToFrequencyMap();
                    if(dependeeToFrequencyMap.containsKey(qualifiedName)) {
                        Integer frequency = dependeeToFrequencyMap.get(qualifiedName);
                        incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.FUNCTION_REFERENCES, frequency.intValue());
                    }
                }
            }
       
            for(int i = 0; i < moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance classInstance = moduleTypeInfo.getNthClassInstance(i);

                for(int j = 0; j < classInstance.getNInstanceMethods(); j++) {
                    QualifiedName resolvingFunctionName = classInstance.getInstanceMethod(j);
                   
                    if (resolvingFunctionName != null) {

                        for (final QualifiedName qualifiedName : targetNames) {
                            if(resolvingFunctionName.equals(qualifiedName)) {
                                incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.INSTANCE_METHOD_REFERENCES, 1);
                            }
                        }
                       
                        if(searchType == SourceMetricFinder.SearchType.ALL) {
                            for (final QualifiedName targetName : targetNames) {
                                TypeClass targetClass = moduleTypeInfo.getVisibleTypeClass(targetName);
                               
                               
                                List<Set<TypeClass>> constraintList = classInstance.getSuperclassPolymorphicVarConstraints();
                                for(int constraintIdx = 0; constraintIdx < constraintList.size(); constraintIdx++) {
                                    Set<TypeClass> constraints = constraintList.get(constraintIdx);
                                   
                                    if(constraints.contains(targetClass)) {
                                        incrementFrequencyCountForSearchResult(results, targetName, SearchResult.Frequency.Type.CLASS_CONSTRAINTS, 1);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        if(searchType == SourceMetricFinder.SearchType.INSTANCES || searchType == SourceMetricFinder.SearchType.ALL) {
           
            for(int i = 0; i < moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance classInstance = moduleTypeInfo.getNthClassInstance(i);
                for (final QualifiedName qualifiedName : targetNames) {
                    if(classInstance.getTypeClass().getName().equals(qualifiedName)) {
                        incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.INSTANCES, 1);
                    }
                }
            }
        }
       
        if(searchType == SourceMetricFinder.SearchType.CLASSES || searchType == SourceMetricFinder.SearchType.ALL) {
           
            for(int i = 0; i < moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance classInstance = moduleTypeInfo.getNthClassInstance(i);
                for (final QualifiedName qualifiedName : targetNames) {
                    TypeExpr typeExpr = classInstance.getType();

                    if(typeExpr.getArity() > 0 && qualifiedName.equals(CAL_Prelude.TypeConstructors.Function)) {
                        incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.CLASSES, 1);
                    }
                   
                    TypeConsApp rootTypeConsApp = typeExpr.rootTypeConsApp();
                    if(rootTypeConsApp == null) {
                        continue;
                    }
                   
                    if(rootTypeConsApp.getName().equals(qualifiedName)) {
                        incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.CLASSES, 1);
                    }
                }
            }
        }
           
        if(searchType == SourceMetricFinder.SearchType.DEFINITION || searchType == SourceMetricFinder.SearchType.ALL) {
            for (final QualifiedName qualifiedName : targetNames) {
                if(qualifiedName.getModuleName().equals(moduleTypeInfo.getModuleName())) {
                    incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.DEFINITION, 1);
                }
            }
        }
       
        if(searchType == SourceMetricFinder.SearchType.ALL) {
       
            for (final QualifiedName qualifiedName : targetNames) {
                if(moduleTypeInfo.doesImportedNameOccur(qualifiedName)) {
                    incrementFrequencyCountForSearchResult(results, qualifiedName, SearchResult.Frequency.Type.IMPORT, 1);
                }
            }
        }
       
        List<SearchResult.Frequency> resultList = new ArrayList<SearchResult.Frequency>();
        for (final Map.Entry<Pair<QualifiedName, Type>, Integer> entry : results.entrySet()) {
            Pair<QualifiedName, SearchResult.Frequency.Type> targetNameAndType = entry.getKey();
            QualifiedName qualifiedName = targetNameAndType.fst();
            SearchResult.Frequency.Type type = targetNameAndType.snd();
           
            int frequency = entry.getValue().intValue();
           
            resultList.add(new SearchResult.Frequency(moduleName, qualifiedName, frequency, type));
        }
       
        return resultList;
    }
   
    /**
     * Helper method for incrementing the frequency count for a particular kind of search hit in a sourceless module.
     * @param map the map to be modified, mapping Pair<QualifiedName, SearchResult.Frequency.Type> to an Integer representing
     *            the frequency of that particular kind of search hit.
     * @param targetName the name of the search hit's target.
     * @param type the type of the search hit.
     * @param freq the frequency increment.
     */
    private void incrementFrequencyCountForSearchResult(LinkedHashMap<Pair<QualifiedName, SearchResult.Frequency.Type>, Integer> map, QualifiedName targetName, SearchResult.Frequency.Type type, int freq) {
        Pair<QualifiedName, SearchResult.Frequency.Type> key = new Pair<QualifiedName, SearchResult.Frequency.Type>(targetName, type);
       
        Integer value = map.get(key);
        if (value == null) {
            map.put(key, Integer.valueOf(freq));
        } else {
            map.put(key, Integer.valueOf(value.intValue() + freq));
        }
    }

    /**
     * @return The ModuleTypeInfos in the current workspace, sorted by
     *          module name.
     */
    public SortedSet<ModuleTypeInfo> getSortedModuleTypeInfos() {
       
        SortedSet<ModuleTypeInfo> sortedModuleTypeInfos = new TreeSet<ModuleTypeInfo>(new Comparator<ModuleTypeInfo>() {
            public int compare(ModuleTypeInfo a, ModuleTypeInfo b) {
                ModuleName leftModuleName = a.getModuleName();
                ModuleName rightModuleName = b.getModuleName();
               
                return leftModuleName.compareTo(rightModuleName);
            }
        });
       
        for(int i = 0; i < moduleContainer.getNModules(); i++) {
            sortedModuleTypeInfos.add(moduleContainer.getNthModuleTypeInfo(i));
        }

        return sortedModuleTypeInfos;
    }
   
    /**
     * Helper function that accepts a list of SearchResults and returns a new list of search results
     * with contextLines filled in.  Assumes that the SearchResults have valid SourcePositions that
     * refer to locations in moduleSource.
     *
     * @param searchResults List of SearchResults to return new, augmented versions of
     * @param moduleSource String containing the source text that corresponds to the SourcePositions in
     *         the SearchResults of searchResults.
     * @return List of SearchResults with their contextLines set based on their SourcePositions in moduleSource
     */
    private static List<SearchResult> augmentSearchResultsWithContextLines(List<? extends SearchResult> searchResults, String moduleSource) {
        List<SearchResult> results = new ArrayList<SearchResult>(searchResults.size());
       
        SourcePosition previousPosition = null;
        int previousIndex = 0;
       
        for (final SearchResult searchResult : searchResults) {
            if (searchResult instanceof SearchResult.Precise) {
                SearchResult.Precise preciseSearchResult = (SearchResult.Precise)searchResult;
               
                SourcePosition currentPosition = preciseSearchResult.getSourcePosition();
               
                SourcePosition lineStart = new SourcePosition(currentPosition.getLine(), 1, currentPosition.getSourceName());
                int startIndex;
                int currentIndex;
               
                if (previousPosition != null && SourcePosition.compareByPosition.compare(previousPosition, lineStart) <= 0) {
                    startIndex = lineStart.getPosition(moduleSource, previousPosition, previousIndex);
                    currentIndex = currentPosition.getPosition(moduleSource, lineStart, startIndex);
                   
                } else {
                    startIndex = lineStart.getPosition(moduleSource);
                    currentIndex = currentPosition.getPosition(moduleSource, lineStart, startIndex);
                }
               
                int endIndex = moduleSource.indexOf('\n', startIndex);
                String line = moduleSource.substring(startIndex, endIndex);
               
                previousPosition = lineStart;
                previousIndex = startIndex;

                results.add(new SearchResult.Precise(preciseSearchResult.getSourceRange(), preciseSearchResult.getName(), preciseSearchResult.getCategory(), line, currentIndex - startIndex, preciseSearchResult.refersToJavaSource()));
               
            } else {
                results.add(searchResult);
            }
        }
       
        return results;
    }
   
    /**
     * Helper function for pre-filtering modules before they have been parsed.  ModuleTypeInfo doesn't contain
     * enough data to tell where in a module's source text a hit occurs, but it does contain enough data to tell
     * whether or not there are any hits in the module.  Scanning this data is a lot faster than parsing a module
     * into a SourceModel and walking it, so before we parse each module we do a quick check to see if there are
     * any hits.
     * <p>
     * NOTE: this method is closely related to {@link #performSourcelessSearch}. Modifying one should require modifications in the other.
     *
     * @param moduleTypeInfo ModuleTypeInfo of module to check for hits
     * @param targetNames List of QualifiedNames of search targets
     * @param searchType Type of search being performed
     * @return boolean true if the module contains at least one search hit for the specified search type and targets, or false otherwise.
     */
    private static boolean moduleContainsHits(ModuleTypeInfo moduleTypeInfo, List<QualifiedName> targetNames, SourceMetricFinder.SearchType searchType) {
       
        if(searchType == SourceMetricFinder.SearchType.REFERENCES || searchType == SourceMetricFinder.SearchType.REFERENCES_CONSTRUCTIONS || searchType == SourceMetricFinder.SearchType.ALL) {
           
            for(int i = 0; i < moduleTypeInfo.getNFunctions(); i++) {
                Function functionEntity = moduleTypeInfo.getNthFunction(i);
                for (final QualifiedName qualifiedName : targetNames) {
                    // Short-circuit return as soon as we know that there is a reference to one
                    // of the target names in this module.
                    if(functionEntity.getDependeeToFrequencyMap().keySet().contains(qualifiedName)) {
                        return true;
                    }
                }
            }
       
            for(int i = 0; i < moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance classInstance = moduleTypeInfo.getNthClassInstance(i);

                for(int j = 0; j < classInstance.getNInstanceMethods(); j++) {
                    QualifiedName resolvingFunctionName = classInstance.getInstanceMethod(j);
                   
                    if (resolvingFunctionName != null) {

                        for (final QualifiedName qualifiedName : targetNames) {
                            // Short-circuit return as soon as we find an instance method that
                            // resolves to one of the target names
                            if(resolvingFunctionName.equals(qualifiedName)) {
                                return true;
                            }
                        }
                       
                        if(searchType == SourceMetricFinder.SearchType.ALL) {
                            for (final QualifiedName targetName : targetNames) {
                                TypeClass targetClass = moduleTypeInfo.getVisibleTypeClass(targetName);
                               
                               
                                List<Set<TypeClass>> constraintArray = classInstance.getSuperclassPolymorphicVarConstraints();
                                for(int constraintIdx = 0; constraintIdx < constraintArray.size(); constraintIdx++) {
                                    Set<TypeClass> constraints = constraintArray.get(constraintIdx);
                                   
                                    if(constraints.contains(targetClass)) {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        if(searchType == SourceMetricFinder.SearchType.INSTANCES || searchType == SourceMetricFinder.SearchType.ALL) {
           
            for(int i = 0; i < moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance classInstance = moduleTypeInfo.getNthClassInstance(i);
                for (final QualifiedName qualifiedName : targetNames) {
                    // Short-circuit return as soon as we know that there is an instance of
                    // one of the target classes in the module
                    if(classInstance.getTypeClass().getName().equals(qualifiedName)) {
                        return true;
                    }
                }
            }
        }
       
        if(searchType == SourceMetricFinder.SearchType.CLASSES || searchType == SourceMetricFinder.SearchType.ALL) {
           
            for(int i = 0; i < moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance classInstance = moduleTypeInfo.getNthClassInstance(i);
                for (final QualifiedName qualifiedName : targetNames) {
                    TypeExpr typeExpr = classInstance.getType();

                    if(typeExpr.getArity() > 0 && qualifiedName.equals(CAL_Prelude.TypeConstructors.Function)) {
                        return true;
                    }
                   
                    TypeConsApp rootTypeConsApp = typeExpr.rootTypeConsApp();
                    if(rootTypeConsApp == null) {
                        continue;
                    }
                   
                    if(rootTypeConsApp.getName().equals(qualifiedName)) {
                        return true;
                    }
                }
            }
        }
           
        if(searchType == SourceMetricFinder.SearchType.DEFINITION || searchType == SourceMetricFinder.SearchType.ALL) {
            for (final QualifiedName qualifiedName : targetNames) {
                if(qualifiedName.getModuleName().equals(moduleTypeInfo.getModuleName())) {
                    return true;
                }
            }
        }
       
        if(searchType == SourceMetricFinder.SearchType.ALL) {
       
            for (final QualifiedName qualifiedName : targetNames) {
                if(moduleTypeInfo.doesImportedNameOccur(qualifiedName)) {
                    return true;
                }
            }
        }
       
        return false;
    }
   
    /**
     * Check whether an entity can be seen in the specified module
     * @param scopedEntity ScopedEntityImpl entity to check visibility of
     * @param moduleTypeInfo ModuleTypeInfo of module to check for visibility from
     * @return true if the module specified by metaModule can see the gem specified by envEntity,
     *          or false otherwise.
     */
    private static boolean isEntityVisibleToModule(ScopedEntity scopedEntity, ModuleTypeInfo moduleTypeInfo) {       
        return moduleTypeInfo.isEntityVisible(scopedEntity);
    }

    /**
     * Find all the instances of the given class.
     * The monitor is called with incremental updates to the search results.
     * @param targetName name (as a String) of the gem to look for references to
     * @param moduleTypeInfos A list of the ModuleTypeInfo's of the modules to search.
     * @param monitor An object that is notified of progress of the search.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public void findInstanceOfClass(String targetName, Collection<ModuleTypeInfo> moduleTypeInfos, IProgressMonitor monitor, CompilerMessageLogger messageLogger){
        if (messageLogger == null){
            throw new NullPointerException("messageLogger must not be null");
        }
       
        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        performSearch(targetEntities, moduleTypeInfos, SourceMetricFinder.SearchType.INSTANCES, monitor, messageLogger);
    }
   
    /**
     * Finds all of the entities in the workspace that have the specified unqualified name
     * @param targetName String possibly-wildcarded name to search for
     * @return List of ScopedEntity of entities with the specified unqualified name
     */
    private List<ScopedEntity> findMatchingEntities(String targetName) {
       
        String unqualifiedName = targetName;
        Matcher moduleMatcher = null;
        int indexOfPeriod = targetName.lastIndexOf('.');
        if(indexOfPeriod != -1) {
            unqualifiedName = targetName.substring(indexOfPeriod + 1);
            String moduleName = targetName.substring(0, indexOfPeriod);
            // we prepend the pattern with (.+\.)? so that if the user specified C.f, then B.C.f and A.B.C.f would also match
            String modulePattern = "(.+\\.)?" + WildcardPatternMatcher.wildcardPatternToRegExp(moduleName);
            moduleMatcher = Pattern.compile(modulePattern, Pattern.CASE_INSENSITIVE).matcher("");
        }
       
        Matcher unqualifiedMatcher = Pattern.compile(WildcardPatternMatcher.wildcardPatternToRegExp(unqualifiedName), Pattern.CASE_INSENSITIVE).matcher("");

        List<ScopedEntity> results = new ArrayList<ScopedEntity>();
       
        // Process modules in sorted order to ensure that the results come
        // back sorted by module name.
        for (final ModuleTypeInfo moduleTypeInfo : getSortedModuleTypeInfos()) {
            if(moduleMatcher != null) {
                moduleMatcher.reset(moduleTypeInfo.getModuleName().toSourceText());
                if(!moduleMatcher.matches()) {
                    continue;
                }
            }
           
            for(int j = 0; j < moduleTypeInfo.getNTypeClasses(); j++) {
                TypeClass typeClass = moduleTypeInfo.getNthTypeClass(j);
                unqualifiedMatcher.reset(typeClass.getName().getUnqualifiedName());
                if(unqualifiedMatcher.matches()) {
                    results.add(typeClass);
                }
            }

            for(int j = 0; j < moduleTypeInfo.getNTypeConstructors(); j++) {
                TypeConstructor typeConstructor = moduleTypeInfo.getNthTypeConstructor(j);
                unqualifiedMatcher.reset(typeConstructor.getName().getUnqualifiedName());
                if(unqualifiedMatcher.matches()) {
                    results.add(typeConstructor);
                }
            }

            FunctionalAgent[] envEntities = moduleTypeInfo.getFunctionalAgents();
            for (final FunctionalAgent envEntity : envEntities) {
                unqualifiedMatcher.reset(envEntity.getName().getUnqualifiedName());
                if(unqualifiedMatcher.matches()) {
                    results.add(envEntity);
                }
            }
        }   
       
        return results;
    }
   
    private List<ScopedEntity> findMatchingEntities(QualifiedName qualifiedName) {
       
        // Ensure that the target gem is visible before doing any serious work
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(qualifiedName.getModuleName());
        if(homeModuleTypeInfo == null){
            return Collections.emptyList();
        }
       
        List<ScopedEntity> targetList = new ArrayList<ScopedEntity>();
       
        FunctionalAgent envEntity = homeModuleTypeInfo.getFunctionalAgent(qualifiedName.getUnqualifiedName());
        if(envEntity != null) {
            targetList.add(envEntity);
        }
       
        TypeClass typeClass = homeModuleTypeInfo.getTypeClass(qualifiedName.getUnqualifiedName());
        if(typeClass != null) {
            targetList.add(typeClass);
        }
       
        TypeConstructor typeCons = homeModuleTypeInfo.getTypeConstructor(qualifiedName.getUnqualifiedName());
        if(typeCons != null) {
            targetList.add(typeCons);
        }

        return targetList;
    }
   
    /**
     * {@inheritDoc}
     * TODOJ The QualifiedName-based API is no longer used and may want to be deleted
     */
    public List<SearchResult> findAllOccurrences(QualifiedName targetGem, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }
       
        List<ScopedEntity> targetEntities = findMatchingEntities(targetGem);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.ALL, messageLogger);
    }
   
    /**
     * {@inheritDoc}
     */
    public List<SearchResult> findAllOccurrences(String targetName, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.ALL, messageLogger);
    }

    /**
     * Find all the occurrences of the specified ScopedEntityImpl.
     * The monitor is called with incremental updates to the search results.
     * @param targetName name (as a String) of the entity to find occurrences of
     * @param moduleTypeInfos A list of the ModuleTypeInfo's of the modules to search.
     * @param monitor An object that is notified of progress of the search.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public void findAllOccurrences(String targetName,
            Collection<ModuleTypeInfo> moduleTypeInfos,
            IProgressMonitor monitor,
            CompilerMessageLogger messageLogger) {
        if (messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        performSearch(targetEntities, moduleTypeInfos, SourceMetricFinder.SearchType.ALL, monitor, messageLogger);
    }

    /**
     * {@inheritDoc}
     * TODOJ The QualifiedName-based API is no longer used and may want to be deleted
     */
    public List<SearchResult> findReferences(QualifiedName targetGem, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetGem);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.REFERENCES, messageLogger);
    }
   
    /**
     * {@inheritDoc}
     */
    public List<SearchResult> findReferences(String targetName, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.REFERENCES, messageLogger);
    }
   
    /**
     * Find all the references to the specified gem in the workspace and return a list
     * of SearchResults of reference to the gem.
     * The monitor is called with incremental updates to the search results.
     * @param targetName name (as a String) of the gem to look for references to
     * @param moduleTypeInfos A list of the ModuleTypeInfo's of the modules to search.
     * @param monitor An object that is notified of progress of the search.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public void findReferences(String targetName,
            Collection<ModuleTypeInfo> moduleTypeInfos,
            IProgressMonitor monitor,
            CompilerMessageLogger messageLogger) {
        if (messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        performSearch(targetEntities, moduleTypeInfos, SourceMetricFinder.SearchType.REFERENCES, monitor, messageLogger);
    }

    /**
     * {@inheritDoc}
     * TODOJ The QualifiedName-based API is no longer used and may want to be deleted
     */
    public List<SearchResult> findInstancesOfClass(QualifiedName targetClass, CompilerMessageLogger messageLogger) {

        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        // Ensure that the target class is visible before doing any serious work
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(targetClass.getModuleName());
        if(homeModuleTypeInfo == null) {
            return Collections.emptyList();
        }
       
        TypeClass typeClass = homeModuleTypeInfo.getTypeClass(targetClass.getUnqualifiedName());
        if(typeClass == null) {
            return Collections.emptyList();
        }

        return performSearch(Collections.<ScopedEntity>singletonList(typeClass), SourceMetricFinder.SearchType.INSTANCES, messageLogger);
    }

    /**
     * {@inheritDoc}
     */
    public List<SearchResult> findInstancesOfClass(String targetName, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.INSTANCES, messageLogger);
    }
   
    /**
     * {@inheritDoc}
     */
    public List<SearchResult> findTypeInstances(String targetName, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.CLASSES, messageLogger);
    }
   
    /**
     * Find all the instances associated with the specified type (i.e., find all of the type classes of
     * which the specified type is an instance) and return a list of SearchResults of the relevant
     * instance definitions.
     * The monitor is called with incremental updates to the search results.
     * @param targetName name (as a String) of the gem to look for references to
     * @param moduleTypeInfos A list of the ModuleTypeInfo's of the modules to search.
     * @param monitor An object that is notified of progress of the search.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public void findTypeInstances(String targetName,
            Collection<ModuleTypeInfo> moduleTypeInfos,
            IProgressMonitor monitor,
            CompilerMessageLogger messageLogger) {
        if (messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        performSearch(targetEntities, moduleTypeInfos,
                SourceMetricFinder.SearchType.CLASSES, monitor, messageLogger);
    }

   
    /**
     * Find all constructions of the specified data constructor and return a list
     * of SearchResults of the expressions.
     * The monitor is called with incremental updates to the search results.
     * @param targetName name (as a String) of the gem to look for constructions of
     * @param moduleTypeInfos A list of the ModuleTypeInfo's of the modules to search.
     * @param monitor An object that is notified of progress of the search.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public void findConstructions(String targetName,
            Collection<ModuleTypeInfo> moduleTypeInfos,
            IProgressMonitor monitor,
            CompilerMessageLogger messageLogger) {
        if (messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        final List<ScopedEntity> allTargetEntities = findMatchingEntities(targetName);
        final List<DataConstructor> targetEntities = convertToOnlyConstructors(allTargetEntities);
        performSearch(targetEntities, moduleTypeInfos,
                SourceMetricFinder.SearchType.REFERENCES_CONSTRUCTIONS, monitor, messageLogger);
    }
   
    /**
     * If the specified target is a constructor then find all constructions of the specified constructor.
     * If the specified target is a type then final all constructions of any constructor that has the
     * given type. 
     * @param targetName name (as a String) of the constructor to look for constructions of
     * or the type to look for constructions of its constructors.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public List<SearchResult> findConstructions(String targetName, CompilerMessageLogger messageLogger){
        final List<ScopedEntity> allTargetEntities = findMatchingEntities(targetName);
        final List<DataConstructor> targetEntities = convertToOnlyConstructors(allTargetEntities);
        return performSearch(targetEntities, SourceMetricFinder.SearchType.REFERENCES_CONSTRUCTIONS, messageLogger);
    }

    /**
     * @param entities list of entities to convert
     * @return a list of contructors from the given list as well as contructors of any types in the given list.
     */
    private List<DataConstructor> convertToOnlyConstructors(final List<ScopedEntity> entities) {
        final List<DataConstructor> targetEntities = new ArrayList<DataConstructor>();
        for (final ScopedEntity entity : entities) {
            if (entity instanceof DataConstructor){
                targetEntities.add((DataConstructor)entity);
            }
            else if (entity instanceof TypeConstructor){
                TypeConstructor typeConstructor = (TypeConstructor) entity;
                final int nDataConstructors = typeConstructor.getNDataConstructors();
                for(int n = 0; n < nDataConstructors; ++n){
                    targetEntities.add(typeConstructor.getNthDataConstructor(n));
                }
            }           
        }
        return targetEntities;
    }

    /**
     * {@inheritDoc}
     * TODOJ The QualifiedName-based API is no longer used and may want to be deleted
     */
    public List<SearchResult> findTypeInstances(QualifiedName targetType, CompilerMessageLogger messageLogger) {
       
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        // Ensure that the target type is visible before doing any serious work
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(targetType.getModuleName());
        if(homeModuleTypeInfo == null){
            return Collections.emptyList();
        }
       
        TypeConstructor typeCons = homeModuleTypeInfo.getTypeConstructor(targetType.getUnqualifiedName());
        if(typeCons == null) {
            return Collections.emptyList();
        }
       
        return performSearch(Collections.<ScopedEntity>singletonList(typeCons), SourceMetricFinder.SearchType.CLASSES, messageLogger);
    }

    class ResultsCollector implements IProgressMonitor {

        private final ArrayList<SearchResult> results = new ArrayList<SearchResult>();
       
        public List<SearchResult> getResults(){
            return results;
        }
       
        public void numberOfModules(int numberOfModules) {
        }

        public void startModule(ModuleName module) {
        }

        public void done() {
        }

        public boolean isCancelled() {
            return false;
        }

        public void moreResults(Collection<? extends SearchResult> moreResults) {
            results.addAll(moreResults);
        }       
    }

    /**
     * {@inheritDoc}
     * TODOJ The QualifiedName-based API is no longer used and may want to be deleted
     */
    public List<SearchResult> findDefinition(Name targetName, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        ResultsCollector results = new ResultsCollector();
        findDefinitionHelper(Collections.singletonList(targetName), results, SourceMetricFinder.SearchType.DEFINITION, messageLogger);
        return results.getResults();
    }

    public List<SearchResult> findDefinition(SearchResult.Precise targetIdentifier, CompilerMessageLogger messageLogger) {
        return findDefinition(targetIdentifier, false, messageLogger);
    }
   
    /**
     * Find the definition of the given targetIdentifier.
     */
    public List<SearchResult> findDefinition(SearchResult.Precise targetIdentifier, boolean getSourceText, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        ResultsCollector results = new ResultsCollector();
        SearchType searchType = SearchType.DEFINITION;
        if (getSourceText){
            searchType = SearchType.SOURCE_TEXT;
        }
        findDefinitionHelper(Collections.singletonList(targetIdentifier.getName()), results, searchType, messageLogger);

        final List<SearchResult> resultsList = results.getResults();
       
        SourceIdentifier.Category category = targetIdentifier.getCategory();
       
        // If there is not category information then return the unfiltered list.
        if (category == null){
            return resultsList;
        }

        // If category information is present use that to reduce the number of options.
       
        final List<SearchResult> filteredList = new ArrayList<SearchResult>();
       
        // LOCAL_DEFINITION and LOCAL_VARIABLE are the same for this purpose
        if (category == SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION){
            category = SourceIdentifier.Category.LOCAL_VARIABLE;
        }
       
        for (final SearchResult result : resultsList) {
            if (result instanceof SearchResult.Precise) {
                SearchResult.Precise preciseResult = (SearchResult.Precise)result;
               
                SourceIdentifier.Category resultCategory = preciseResult.getCategory();
                // LOCAL_DEFINITION and LOCAL_VARIABLE are the same for this purpose
                if (resultCategory == SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION){
                    resultCategory = SourceIdentifier.Category.LOCAL_VARIABLE;
                }

                if (resultCategory == category || resultCategory == null){
                    filteredList.add(preciseResult);
                }
            }
        }

        return filteredList;
    }
 
    /**
     * Find the type declaration for the given object if any.
     * @param functionName the object to get the type declaration of
     * @param messageLogger
     * @return the type declaration position of the given object or null if not found.
     */
    public SearchResult.Precise findTypeDeclaration(QualifiedName functionName, CompilerMessageLogger messageLogger) {
        // See if we can find the start of the type declaration and include that.
        MessageLogger logger = new MessageLogger();
        final SourceModel.ModuleDefn sourceModel = moduleContainer.getSourceModel(functionName.getModuleName(), true, logger);
        // if there are errors then skip getting the type.
        if (logger.getNErrors() == 0){
            final int nDefs = sourceModel.getNTopLevelDefns();
            for(int iType = 0; iType < nDefs; ++iType){
                final TopLevelSourceElement element = sourceModel.getNthTopLevelDefn(iType);
                if (element instanceof FunctionTypeDeclaration){
                    FunctionTypeDeclaration typeDeclaration = (FunctionTypeDeclaration) element;
                    if (typeDeclaration.getFunctionName().equals(functionName.getUnqualifiedName())){
                        SourceRange sourceRangeOfType = typeDeclaration.getSourceRangeOfDefn();
                        return new Precise(sourceRangeOfType, functionName, null, false);
                    }
                }
            }
        }
        return null;
    }
           
    /**
     * {@inheritDoc}
     */
    public List<SearchResult> findDefinition(String targetName, CompilerMessageLogger messageLogger) {
        if(messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        Set<Name> qualifiedTargets = new LinkedHashSet<Name>();
       
        for (final ScopedEntity scopedEntity : targetEntities) {
            qualifiedTargets.add(scopedEntity.getName());
        }
       
        ResultsCollector results = new ResultsCollector();
        findDefinitionHelper(new ArrayList<Name>(qualifiedTargets), results, SourceMetricFinder.SearchType.DEFINITION, messageLogger);
        return results.getResults();
    }
   
    /**
     * Find the position at which the specified typeclass, type, data constructor, function, or method was defined.
     * The monitor is called with incremental updates to the search results.
     * @param targetName name (as a String) of the gem to look for references to
     * @param moduleNames A list of the names of the modules to search.
     * @param monitor An object that is notified of progress of the search.
     * @param messageLogger Used to log messages describing conditions encountered
     *                       during searching (eg, unparseable module file encountered).
     *                       This argument must not be null.
     */
    public void findDefinition(String targetName,
            Collection<ModuleName> moduleNames,
            IProgressMonitor monitor,
            CompilerMessageLogger messageLogger) {
        if (messageLogger == null) {
            throw new NullPointerException("messageLogger must not be null");
        }

        List<ScopedEntity> targetEntities = findMatchingEntities(targetName);
        Set<QualifiedName> qualifiedTargets = new LinkedHashSet<QualifiedName>();

        for (final ScopedEntity scopedEntity : targetEntities) {
            if (moduleNames.contains(scopedEntity.getName().getModuleName())){
                qualifiedTargets.add(scopedEntity.getName());
            }
        }

        findDefinitionHelper(new ArrayList<Name>(qualifiedTargets), monitor, SourceMetricFinder.SearchType.DEFINITION, messageLogger);
    }

    /**
     * Find the definitions of each of qualifiedTargets. This is a helper
     * function that the two versions of findDefinition delegate to.
     *
     * @param qualifiedTargets
     *           List of Names to search for. These should be grouped
     *            by module name (ie, [M2.a, M2.d, M2.c, Prelude.b] is okay, but
     *            [M2.a, Prelude.b, M2.c, M2.d] is not)
     */
    private void findDefinitionHelper(Collection<Name> qualifiedTargets,
            IProgressMonitor monitor,
            SearchType searchType,
            CompilerMessageLogger messageLogger) {

        ModuleName prevModuleName = null;

        if (monitor != null){
            monitor.numberOfModules(qualifiedTargets.size());
        }
        List<Name> moduleTargets = new ArrayList<Name>(); // Targets in the current module
        for (final Name targetName : qualifiedTargets) {

            ModuleName homeModuleName = targetName.getModuleName();

            if (monitor != null){
                monitor.startModule(homeModuleName);
                if (monitor.isCancelled()){
                    return;
                }
            }
           
            if (homeModuleName.equals(prevModuleName)) {
                moduleTargets.add(targetName);

            } else {
                if (prevModuleName != null) {
                    monitor.moreResults(findDefinitionsInSingleModule(prevModuleName, moduleTargets, searchType, messageLogger));
                }

                moduleTargets.clear();
                prevModuleName = homeModuleName;
                moduleTargets.add(targetName);
            }
        }

        if (moduleTargets.size() > 0) {
            monitor.moreResults(findDefinitionsInSingleModule(prevModuleName, moduleTargets, searchType, messageLogger));
        }
    }
       
    /**
     * Search for the definitions of qualifiedTargets in the module named moduleName.
     * @param moduleName Name of the module to search
     * @param qualifiedTargets List of Names of entities to find in the specified module.
     *                          Each of these Names should have moduleName as their module name.
     * @return A List of SearchResults (these will include contextLines)
     */
    private List<? extends SearchResult> findDefinitionsInSingleModule(ModuleName moduleName, List<Name> qualifiedTargets, SearchType searchType, CompilerMessageLogger messageLogger) {

        MessageLogger moduleLogger = new MessageLogger();
       
        // Parse a SourceModel to process
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(moduleName);
        if (homeModuleTypeInfo == null){
            return Collections.emptyList();
        }
        SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, true, moduleLogger);
        // if the module is a sourceless module, then we need to process it differently.
        if (moduleDefn == null && moduleLogger.getNErrors() == 0) {
            final List<QualifiedName> qualifiedNamesOnly = new ArrayList<QualifiedName>();
            for (final Name name : qualifiedTargets) {
                if (name instanceof QualifiedName) {
                    qualifiedNamesOnly.add((QualifiedName)name);
                }
            }
            return performSourcelessSearch(homeModuleTypeInfo, qualifiedNamesOnly, SourceMetricFinder.SearchType.DEFINITION);
        }
       
        if(moduleDefn == null) {
            // Copy messages, adding source names to each SourcePosition
            for (final CompilerMessage compilerMessage : moduleLogger.getCompilerMessages()) {
                SourceRange sourceRange = compilerMessage.getSourceRange();
                if(sourceRange != null) {
                    SourceRange augmentedRange = getAugmentedRange(sourceRange, moduleName);
                    messageLogger.logMessage(new CompilerMessage(augmentedRange, compilerMessage.getMessageKind()));
                } else {
                    messageLogger.logMessage(compilerMessage);
                }
            }
            return Collections.emptyList();
        }
           
        // Find the hits
        return augmentSearchResultsWithContextLines(SourceMetricFinder.performSearch(moduleDefn, homeModuleTypeInfo, searchType, qualifiedTargets), moduleContainer.getModuleSource(moduleName));       
    }

    /**
     * Search for the definitions of the selected symbol if one is selected.
     * @param moduleName Name of the module to search
     * @param line The line number of the symbol to search for. The first line in the file is line number one.
     * @param column The column number of the symbol to search for. The first column in the line is column number one.
     * @return A SearchResult.Precise object for the definition of the selected symbol or null if not found.
     */
    public SearchResult findDefinition(ModuleName moduleName, int line, int column, CompilerMessageLogger messageLogger) {

        SearchResult.Precise[] result = findSymbolAt(moduleName, line, column, messageLogger);
        if (result == null) {
            return null;
        }
       
        // Search for the definition of the given symbol. The result[0] is a hack.
        // The hack used to be implicit in findSymbolAt. TODO GJM: fix this
        List<SearchResult> definitions = findDefinition(result[0].getName(), messageLogger);
       
        // todo-jowong this is a workaround since we do not have kind info on the name found
        // (i.e. for a cons name whether it's a type cons, data cons or type class)
        // for the time being, we just return the first one - the name is likely
        // both a valid type cons and a valid data cons (and potentially also a valid type class).
        if (definitions.size() >= 1){
            return definitions.get(0);
        }
        else{
            return null;
        }
    }

    /**
     * Find the position of a given entity.
     * @param name The object to find the position of.
     * @return The position of the entity. Maybe be null if the entity does not correspond to source.
     */
    public SearchResult.Precise getPosition(QualifiedName name, Category categoryExpected){
        MessageLogger messageLogger = new MessageLogger();
        // Search for the definition of the given symbol
        List<SearchResult> definitions = findDefinition(name, messageLogger);
       
        // todo-jowong this is a workaround since we do not have kind info on the name found
        // (i.e. for a cons name whether it's a type cons, data cons or type class)
        // for the time being, we just return the first one - the name is likely
        // both a valid type cons and a valid data cons (and potentially also a valid type class).
        if (definitions.size() >= 1){
            for (int i = 0; i < definitions.size(); i++) {
                Object definition = definitions.get(i);
                // Pick out the first SearchResult object
                if (definition instanceof SearchResult.Precise){
                    final SourceIdentifier.Category categoryFound = ((SearchResult.Precise) definition).getCategory();
                    if (categoryFound != null){
                        if (categoryFound != categoryExpected){
                            continue;
                        }
                    }

                    return (SearchResult.Precise) definition;
                }
            }
        }
       
        return null;
    }

    /**
     * Find the position of a given class instance.
     * @return The position of the ClassInstance. Maybe be null if the entity does not correspond to source.
     */
    public SourceRange getPosition(ClassInstance classInstance, CompilerMessageLogger logger){
        SourceModel.ModuleDefn moduleSourceModel = moduleContainer.getSourceModel(classInstance.getModuleName(), true, logger);
        ModuleTypeInfo moduleTypeInfo = moduleContainer.getModuleTypeInfo(classInstance.getModuleName());
        if (moduleTypeInfo == null){
            return null;
        }
        for( int i = 0; i < moduleSourceModel.getNTopLevelDefns(); ++i){
            TopLevelSourceElement topLevelDefn = moduleSourceModel.getNthTopLevelDefn(i);
            if (topLevelDefn instanceof SourceModel.InstanceDefn){
                SourceModel.InstanceDefn instanceDefn = (InstanceDefn) topLevelDefn;
                if (same(classInstance, instanceDefn, moduleTypeInfo)){
                    return instanceDefn.getSourceRangeOfName();
                }
            }
        }
        return null;
    }
   
    /**
     * Checks if the given class instance (from the compiler) and the instance definition (from the source model)
     * refer the the same instance.
     * @param classInstance
     * @param instanceDefn
     * @return true if the classInstance and instanceDefn refer to the same object.
     *
     * TODO put this function in a better place
     */
    public static boolean same(ClassInstance classInstance, InstanceDefn instanceDefn, ModuleTypeInfo moduleTypeInfo){
        // check the type class names
       
            final QualifiedName name = classInstance.getTypeClass().getName();
            SourceModel.Name.TypeClass typeClassName = instanceDefn.getTypeClassName();
            ModuleName typeClass_moduleName = resolveModuleName(moduleTypeInfo, typeClassName);
            if (!name.getModuleName().equals(typeClass_moduleName)){
                return false;
            }
            if (!name.getUnqualifiedName().equals(typeClassName.getUnqualifiedName())){
                return false;
            }
       
        // check instance type cons
        {
            InstanceTypeCons instanceTypeCons = instanceDefn.getInstanceTypeCons();
            if (instanceTypeCons instanceof InstanceTypeCons.TypeCons){
                InstanceTypeCons.TypeCons typeCons = (TypeCons) instanceTypeCons;
                SourceModel.Name.TypeCons typeConsName = typeCons.getTypeConsName();
                ModuleName typeCons_moduleName = resolveModuleName(moduleTypeInfo, typeConsName);
                QualifiedName typeConsQualifiedName = QualifiedName.make(typeCons_moduleName, typeConsName.getUnqualifiedName());
                return classInstance.getIdentifier().equals(new ClassInstanceIdentifier.TypeConstructorInstance(QualifiedName.make(typeClass_moduleName, typeClassName.getUnqualifiedName()), typeConsQualifiedName));               
            }
            if (instanceTypeCons instanceof InstanceTypeCons.Function){
                return classInstance.getIdentifier().equals(new ClassInstanceIdentifier.TypeConstructorInstance(QualifiedName.make(typeClass_moduleName, typeClassName.getUnqualifiedName()), CAL_Prelude.TypeConstructors.Function));
            }
            if (instanceTypeCons instanceof InstanceTypeCons.Unit){
                return classInstance.getIdentifier().equals(new ClassInstanceIdentifier.TypeConstructorInstance(QualifiedName.make(typeClass_moduleName, typeClassName.getUnqualifiedName()), CAL_Prelude.TypeConstructors.Unit));
            }
            if (instanceTypeCons instanceof InstanceTypeCons.List){
                return classInstance.getIdentifier().equals(new ClassInstanceIdentifier.TypeConstructorInstance(QualifiedName.make(typeClass_moduleName, typeClassName.getUnqualifiedName()), CAL_Prelude.TypeConstructors.List));
            }
            if (instanceTypeCons instanceof InstanceTypeCons.Record){
                return classInstance.getIdentifier() instanceof UniversalRecordInstance;
            }
        }
        return true;
    }

    // TODO make this into utility functions that are more accessible
    private static ModuleName resolveModuleName(ModuleTypeInfo moduleTypeInfo, SourceModel.Name.TypeClass typeClassName) {
        ModuleName moduleName = SourceModel.Name.Module.maybeToModuleName(typeClassName.getModuleName());
        if (moduleName == null) {
            if (moduleTypeInfo.getTypeClass(typeClassName.getUnqualifiedName()) != null) {
                return moduleTypeInfo.getModuleName();
            } else {
                return moduleTypeInfo.getModuleOfUsingTypeClass(typeClassName.getUnqualifiedName());
            }
        } else {
            return moduleTypeInfo.getModuleNameResolver().resolve(moduleName).getResolvedModuleName();
        }
    }   
   
    // TODO make this into utility functions that are more accessible
    private static ModuleName resolveModuleName(ModuleTypeInfo moduleTypeInfo, SourceModel.Name.TypeCons typeConsName) {
        ModuleName moduleName = SourceModel.Name.Module.maybeToModuleName(typeConsName.getModuleName());
        if (moduleName == null) {
            if (moduleTypeInfo.getTypeConstructor(typeConsName.getUnqualifiedName()) != null) {
                return moduleTypeInfo.getModuleName();
            } else {
                return moduleTypeInfo.getModuleOfUsingTypeConstructor(typeConsName.getUnqualifiedName());
            }
        } else {
            return moduleTypeInfo.getModuleNameResolver().resolve(moduleName).getResolvedModuleName();
        }
    }
   
    /**
     * the search result from the list that covers the smallest area. This is used to winnow
     * the list to the match that is closest to the cursor. If more than once item has the same smallest
     * range then a random one is selected. This code assumes that the search results all nest properly
     * within each other. That is the kind of list that would be returned by findSymbolAt.
     *
     * @param results the list of results to check.
     * @return the search result from the list that covers the smallest area.
     */
    public static SearchResult.Precise selectMostPrecise(SearchResult.Precise[] results){
        if (results.length == 0){
            throw new IllegalArgumentException();
        }
       
        // Start with this one and then scan through the list looking for
        // the item that is most inside the other ones.
        SourceRange longestRange = results[0].getSourceRange();
        SearchResult.Precise result = results[0];
        for(int i = 1; i < results.length; ++i){
            final SearchResult.Precise sr = results[i];
            final int compareStart = SourcePosition.compareByPosition.compare(sr.getSourceRange().getStartSourcePosition(), longestRange.getStartSourcePosition());
            switch(compareStart){
            case 0:
                final int compareEnd = SourcePosition.compareByPosition.compare(sr.getSourceRange().getEndSourcePosition(), longestRange.getEndSourcePosition());
                switch(compareEnd){
                case 0:
                    // current one is okay.
                    continue;
                case 1:
                    continue;
                }
            case -1:
                continue;
            }
            longestRange = sr.getSourceRange();
            result = sr;
        }
       
        return result;
    }
   
    /**
     * Finds the symbol at the specified line and column.
     * @param moduleName Name of the module to search
     * @param line The line number of the symbol to search for. The first line in the file is line number one.
     * @param column The column number of the symbol to search for. The first column in the line is column number one.
     * @param messageLogger
     * @return An array of SearchResult.Precise objects for the symbol at the specified line and column.
     */
    public SearchResult.Precise[] findSymbolAt(ModuleName moduleName, int line, int column, CompilerMessageLogger messageLogger) {
        MessageLogger moduleLogger = new MessageLogger();
       
        // Parse a SourceModel to process
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(moduleName);
        if (homeModuleTypeInfo == null){
            return null;
        }
        SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, false, moduleLogger);
       
        if(moduleDefn == null) {
            // Copy messages, adding source names to each SourcePosition
            for (final CompilerMessage compilerMessage : moduleLogger.getCompilerMessages()) {
                SourceRange sourceRange = compilerMessage.getSourceRange();
                if(sourceRange != null) {
                    SourceRange augmentedRange = getAugmentedRange(sourceRange, moduleName);
                    messageLogger.logMessage(new CompilerMessage(augmentedRange, compilerMessage.getMessageKind()));
                } else {
                    messageLogger.logMessage(compilerMessage);
                }
            }
            return null;
        }

        // Find the symbol at the given position
        List<SearchResult.Precise> results = SourceMetricFinder.findSymbolAt(moduleDefn, homeModuleTypeInfo, new SourcePosition(line, column, moduleName));

        if (results.size() == 0){
            // no symbol found
            return null;
        }
       
        return results.toArray(new SearchResult.Precise[results.size()]);
    }

    /**
     * Finds the position of the next top level element. This may return null.
     * @param moduleName Name of the module to search
     * @param line The line number of the symbol to search after. The first line in the file is line number one.
     * @param column The column number of the symbol to search after. The first column in the line is column number one.
     * @param messageLogger
     * @return The source range of the name of the next top level element. This may be null.
     */
    public SourceRange findNextTopLevelElement(ModuleName moduleName, int line, int column, CompilerMessageLogger messageLogger) {
        // Parse a SourceModel to process
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(moduleName);
        if (homeModuleTypeInfo == null){
            return null;
        }
        SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, false, messageLogger);
       
        if(moduleDefn == null) {
            return null;
        }

        // Find the position of the next top level function
        return SourceMetricFinder.findNextTopLevelElement(moduleDefn, new SourcePosition(line, column, moduleName));
    }

    /**
     * Finds the position of the previous top level element. This may return null.
     * @param moduleName Name of the module to search
     * @param line The line number of the symbol to search before. The first line in the file is line number one.
     * @param column The column number of the symbol to search before. The first column in the line is column number one.
     * @param messageLogger
     * @return The source range of the name of the previous top level element. This may be null.
     */
    public SourceRange findPreviousTopLevelElement(ModuleName moduleName, int line, int column, CompilerMessageLogger messageLogger) {
        // Parse a SourceModel to process
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(moduleName);
        if (homeModuleTypeInfo == null){
            return null;
        }
        SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, false, messageLogger);
       
        if(moduleDefn == null) {
            return null;
        }

        // Find the position of the next top level function
        return SourceMetricFinder.findPreviousTopLevelElement(moduleDefn, new SourcePosition(line, column, moduleName));
    }

    /**
     * Finds the source element that contains the given position. This may return null.
     * @param moduleName Name of the module to search
     * @param line The line number of the symbol to search before. The first line in the file is line number one.
     * @param column The column number of the symbol to search before. The first column in the line is column number one.
     * @param messageLogger
     * @return A pair where the first element is the source element that
     * contains the given position and the second element is the
     * source range of the element. This may be null.
     */
    public Pair<SourceElement, SourceRange> findContainingSourceElement(ModuleName moduleName, int line, int column, CompilerMessageLogger messageLogger) {
        // Parse a SourceModel to process
        ModuleTypeInfo homeModuleTypeInfo = moduleContainer.getModuleTypeInfo(moduleName);
        if (homeModuleTypeInfo == null){
            return null;
        }
        SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, false, messageLogger);
       
        if(moduleDefn == null) {
            return null;
        }

        return SourceMetricFinder.findContainingSourceElement(moduleDefn, new SourcePosition(line, column, moduleName));
    }
   
    /**
     * Search for the definitions of qualifiedTargets in the module named moduleName.
     * This needs some more work after the source model has better position information.
     * @param moduleName Name of the module to search
     * @return A path to the appropriate element. This maybe be null.
     */
    // todo-jowong look at how Greg implemented this method when refactoring to use SearchManager...
    public SourceElement[] findContainingSourceElement(ModuleName moduleName, int line, int column) {
        MessageLogger moduleLogger = new MessageLogger();
       
        // Parse a SourceModel to process
        ModuleTypeInfo moduleTypeInfo = moduleContainer.getModuleTypeInfo(moduleName);
        if (moduleTypeInfo == null){
            return null;
        }

        SourceModel.ModuleDefn moduleDefn = moduleContainer.getSourceModel(moduleName, true, moduleLogger);
        // if the module is a sourceless module, then we need to process it differently
        // or this is a compile error
        if (moduleDefn == null) {
            return null;
        }
       
        // Find the top level element
        final int nTopLevelDefns = moduleDefn.getNTopLevelDefns();
        for(int i = 0; i < nTopLevelDefns; ++i){
            TopLevelSourceElement tlse = moduleDefn.getNthTopLevelDefn(i);
            SourceRange sourceRange = tlse.getSourceRangeOfDefn();
            SourcePosition pos = new SourcePosition(line, column);
            if (sourceRange != null && sourceRange.containsPosition(pos)){
                // The scope of the Algebraic type includes the data constructors so search for the possible
                // data constructor match first before going for the algebraic type
                if (tlse instanceof SourceModel.TypeConstructorDefn.AlgebraicType){
                    SourceModel.TypeConstructorDefn.AlgebraicType at = (SourceModel.TypeConstructorDefn.AlgebraicType) tlse;
                    // This is a hacky way of finding out where the cursor is until the source model is richer.
                    final int nDataConstructors = at.getNDataConstructors();
                    for(int j = 0; j < nDataConstructors; ++j){
                        DataConsDefn dcd = at.getNthDataConstructor(j);
                        if (SourcePosition.compareByPosition.compare(dcd.getSourceRangeOfName().getStartSourcePosition(), pos) <= 0){
                            SourcePosition end = null;
                            if (dcd.getNTypeArgs() != 0){
                                // look for the position of the last arg
                                end = dcd.getNthTypeArg(dcd.getNTypeArgs()-1).getSourcePosition();
                            }
                            if (end == null){
                                // no args so just just the position of the end of the data cons.
                                end = dcd.getSourceRangeOfDefn().getEndSourcePosition();
                            }
                            
                            if (SourcePosition.compareByPosition.compare(pos, end) <= 0){
                                return new SourceElement[] {tlse, dcd};
                            }
                        }
                    }
                }
                else if (tlse instanceof SourceModel.TypeClassDefn){
                    SourceModel.TypeClassDefn at = (SourceModel.TypeClassDefn) tlse;
                    // This is a hacky way of finding out where the cursor is until the source model is richer.
                    final int nClassMethods = at.getNClassMethodDefns();
                    for(int j = 0; j < nClassMethods; ++j){
                        ClassMethodDefn classMethod = at.getNthClassMethodDefn(j);
                        if (SourcePosition.compareByPosition.compare(classMethod.getSourceRangeOfName().getStartSourcePosition(), pos) <= 0){
                            if (classMethod.getSourceRangeOfClassDefn().containsPosition(pos)){
                                return new SourceElement[] {tlse, classMethod};
                            }
                        }
                    }
                }
                // no else this is the default for when the if fails
                return new SourceElement[] {tlse};                   
               
            }
        }
       
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return dumpReferenceFrequencies(new AcceptAllModulesFilter(), new AcceptAllQualifiedNamesFilter(), false);
    }
   
   
    /**
     * TODO-AE
     * INTERNAL METHOD � Not part of public API
     * <p>
     * The {@link org.openquark.cal.compiler.SourceModel.SourceElement#getSourceRange()} method is package protected for
     * various reasons.  However, it is sometimes necessary to get the source range
     * of an element.  This method serves as a back door to get it.
     * <p>
     * If and when {@link org.openquark.cal.compiler.SourceModel.SourceElement#getSourceRange()} becomes public, this
     * method will go away.
     *
     * @param elt the {@link org.openquark.cal.compiler.SourceModel.SourceElement} whose source range to get
     * @return the element's source range
     */
    public static SourceRange getSourceRange(SourceElement elt) {
        return elt.getSourceRange();
    }
}

TOP

Related Classes of org.openquark.cal.compiler.SourceMetricsManager$ResultsCollector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.