/* * 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. */ /* * NavHtmlFactory.java * Creation date: Jul 23, 2003 * By: Frank Worsley */ package org.openquark.gems.client.navigator; import java.net.URL; import java.text.ChoiceFormat; import java.text.DateFormat; import java.text.Format; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.regex.Pattern; import org.openquark.cal.caldoc.CALDocToHTMLUtilities; import org.openquark.cal.compiler.CALDocComment; import org.openquark.cal.compiler.ClassInstance; import org.openquark.cal.compiler.ClassInstanceIdentifier; import org.openquark.cal.compiler.ClassMethod; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.compiler.FunctionalAgent; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.ModuleTypeInfo; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.ScopedEntity; import org.openquark.cal.compiler.ScopedEntityNamingPolicy; import org.openquark.cal.compiler.TypeClass; import org.openquark.cal.compiler.TypeConsApp; import org.openquark.cal.compiler.TypeConstructor; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.compiler.ScopedEntityNamingPolicy.UnqualifiedUnlessAmbiguous; import org.openquark.cal.metadata.ArgumentMetadata; import org.openquark.cal.metadata.CALExample; import org.openquark.cal.metadata.CALExpression; import org.openquark.cal.metadata.CALFeatureMetadata; import org.openquark.cal.metadata.ClassInstanceMetadata; import org.openquark.cal.metadata.ClassMethodMetadata; import org.openquark.cal.metadata.DataConstructorMetadata; import org.openquark.cal.metadata.FunctionMetadata; import org.openquark.cal.metadata.FunctionalAgentMetadata; import org.openquark.cal.metadata.InstanceMethodMetadata; import org.openquark.cal.metadata.ModuleMetadata; import org.openquark.cal.metadata.ScopedEntityMetadata; import org.openquark.cal.metadata.TypeClassMetadata; import org.openquark.cal.metadata.TypeConstructorMetadata; import org.openquark.cal.services.CALFeatureName; import org.openquark.cal.services.CALWorkspace; import org.openquark.cal.services.MetaModule; import org.openquark.gems.client.GemCutter; import org.openquark.util.WildcardPatternMatcher; import org.openquark.util.html.HtmlHelper; /** * This class is a factory for generating the pages displayed in the CAL navigator. * It accepts a navigator address and returns the correct HTML formatted page for it. * * @author Frank Worsley */ public class NavHtmlFactory { /* Anchors used in the HTML output. */ private static final String DESCRIPTION_ANCHOR = "#description"; private static final String RESULT_ANCHOR = "#result"; private static final String ARGUMENTS_ANCHOR = "#arguments"; private static final String REQUIRED_METHOD_ANCHOR = "#requiredMethod"; private static final String EXAMPLES_ANCHOR = "#examples"; private static final String GENERAL_ANCHOR = "#general"; private static final String RELATED_ANCHOR = "#relatedFeatures"; private static final String CUSTOM_ATTRIBUTES_ANCHOR = "#customAttributes"; private static final String IMPORTS_ANCHOR = "#importedModules"; private static final String FRIENDS_ANCHOR = "#friendModules"; private static final String PARENTS_ANCHOR = "#parentClasses"; private static final String METHODS_ANCHOR = "#classMethods"; private static final String CONSTRUCTORS_ANCHOR = "#dataConstructors"; private static final String FUNCTIONS_ANCHOR = "#functions"; private static final String TYPES_ANCHOR = "#typeConstructors"; private static final String CLASSES_ANCHOR = "#typeClasses"; private static final String INSTANCES_ANCHOR = "#classInstances"; private static final String MODULES_ANCHOR = "#modules"; private static final String INSTANCE_TYPE_ANCHOR = "#instanceType"; private static final String INSTANCE_CLASS_ANCHOR = "#instanceClass"; private static final String INSTANCE_METHODS_ANCHOR = "#instanceMethods"; /** A DateFormat for emitting time in the current locale. */ private static final DateFormat LOCALE_DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM); /** * Implements a cross-reference generator capable of generating hyperlinks for cross-references * represented as CALFeatureNames. The implementation simply bridges to existing methods for generating * references in the outer class. * * @author Joseph Wong */ private static final class CrossReferenceGenerator extends CALDocToHTMLUtilities.CALFeatureCrossReferenceGenerator { /** * The owner of the navigator. */ private final NavFrameOwner owner; /** * Constructs a cross-reference generator capable of generating hyperlinks for cross-references * represented as CALFeatureNames. * @param owner the owner of the navigator. */ private CrossReferenceGenerator(NavFrameOwner owner) { this.owner = owner; } /** * Returns whether the given qualified name is a class method name. * @param qualifiedName the name to check. * @return true if the given qualified name is a class method name, false otherwise. */ public boolean isClassMethodName(QualifiedName qualifiedName) { return isNameForClassMethod(owner, qualifiedName); } /** * Builds a well-formed HTML fragment for the cross-reference, given here as a CALFeatureName. * @param featureName the cross-reference. * @param moduleNameInSource how the module name portion of the reference appears in source. Could be the empty string if the reference is unqualified in source. * @return the HTML fragment for the cross-reference. */ public String getRelatedFeatureLinkHTML(CALFeatureName featureName, String moduleNameInSource) { return getRelatedFeatureLinkHtml(owner, featureName, moduleNameInSource); } } /** * Implements a cross-reference generator capable of generating non-hyperlinked text for cross-references * represented as CALFeatureNames. * * @author Joseph Wong */ private static final class NoHyperlinkCrossReferenceGenerator extends CALDocToHTMLUtilities.CALFeatureCrossReferenceGenerator { /** * Since whether a qualified name refers to a class method or not is irrelevant for this generator (as it * simply returns the display name of any given cross-reference), false is returned for all input values. * @param qualifiedName the name. * @return false, always. */ public boolean isClassMethodName(QualifiedName qualifiedName) { return false; } /** * Builds a well-formed HTML fragment for the cross-reference, given here as a CALFeatureName. * @param featureName the cross-reference. * @param moduleNameInSource how the module name portion of the reference appears in source. Could be the empty string if the reference is unqualified in source. * @return the HTML fragment for the cross-reference. */ public String getRelatedFeatureLinkHTML(CALFeatureName featureName, String moduleNameInSource) { if (featureName.getType() == CALFeatureName.MODULE) { if (moduleNameInSource.length() == 0) { return featureName.toModuleName().toSourceText(); } else { return moduleNameInSource; } } else if (featureName.isScopedEntityName()) { String unqualifiedName = featureName.toQualifiedName().getUnqualifiedName(); if (moduleNameInSource.length() == 0) { return unqualifiedName; } else { return moduleNameInSource + '.' + unqualifiedName; } } else { return featureName.getName(); } } } /** * Returns whether the given qualified name is a class method name. * @param owner the navigator owner. * @param qualifiedName the name to check. * @return true if the given qualified name is a class method name, false otherwise. */ private static boolean isNameForClassMethod(NavFrameOwner owner, QualifiedName qualifiedName) { MetaModule metaModule = owner.getPerspective().getMetaModule(qualifiedName.getModuleName()); if (metaModule == null) { return false; } return metaModule.getTypeInfo().getClassMethod(qualifiedName.getUnqualifiedName()) != null; } /** * @param owner the navigator owner * @param url the navigator url of the entity to display metadata for * @return an HTML formatted description of the metadata stored in the metadata object. */ public static String getPage(NavFrameOwner owner, NavAddress url) { if (url.getMethod() == NavAddress.WORKSPACE_METHOD) { return getWorkspacePage(owner); } else if (url.getMethod() == NavAddress.SEARCH_METHOD) { return getSearchPage(owner, url); } else if (url.getParameter(NavAddress.VAULT_PARAMETER) != null) { return getVaultPage(owner, url); } else if (url.getMethod() == NavAddress.MODULE_NAMESPACE_METHOD) { // this is a namespace node representing a module name without a corresponding actual module return getNamespacePage(owner, url, url.toFeatureName().toModuleName()); } else if (NavAddressHelper.getMetadata(owner, url) != null) { return getMetadataPage(owner, url); } else if (url.getMethod() == NavAddress.COLLECTOR_METHOD) { return getCollectorNotFoundPage(url); } else { return getErrorPage(url); } } /** * @param owner the navigator owner * @param url the navigator url to display metadata for * @return the HTML for the metadata display page */ private static String getMetadataPage(NavFrameOwner owner, NavAddress url) { CALFeatureMetadata metadata = NavAddressHelper.getMetadata(owner, url); StringBuilder buffer = new StringBuilder(); // display the page header buffer.append(getMetadataPageHeader(owner, url)); // display the name as the heading final String title = NavAddressHelper.getDisplayText(owner, url, ScopedEntityNamingPolicy.UNQUALIFIED); generateTitleWithIcon(buffer, url, title); if (url.getMethod() == NavAddress.COLLECTOR_METHOD) { // if the url refers to a collector, then there is no CALDoc for it, so simply // display the metadata if (metadata instanceof FunctionalAgentMetadata) { buffer.append(getBasicMetadataHtml(owner, metadata, null)); buffer.append(getFunctionalAgentMetadataHtml(owner, (FunctionalAgentMetadata) metadata, url, null, false)); buffer.append(getAdditionalMetadataHtml(owner, metadata, null)); } } else { // add the basic metadata, then the metadata specific sections, and finally put the additional sections at the bottom CALFeatureName featureName = url.toFeatureName(); // in the following if-else checks, if the metadata is null, it will fail all the instanceof checks // and simply fall off the end with no additional HTML generated, which is what is intended in such cases. if (metadata instanceof FunctionalAgentMetadata) { QualifiedName qualifiedName = featureName.toQualifiedName(); ModuleTypeInfo moduleTypeInfoForFeature = owner.getPerspective().getMetaModule(qualifiedName.getModuleName()).getTypeInfo(); FunctionalAgent envEntity = moduleTypeInfoForFeature.getFunctionalAgent(qualifiedName.getUnqualifiedName()); boolean isRequiredClassMethod = false; if (envEntity instanceof ClassMethod) { ClassMethod method = (ClassMethod)envEntity; isRequiredClassMethod = (method.getDefaultClassMethodName() == null); // no default -> required } CALDocComment caldoc = envEntity.getCALDocComment(); buffer.append(getBasicMetadataHtml(owner, metadata, caldoc)); buffer.append(getFunctionalAgentMetadataHtml(owner, (FunctionalAgentMetadata) metadata, url, caldoc, isRequiredClassMethod)); buffer.append(getAdditionalMetadataHtml(owner, metadata, caldoc)); } else if (metadata instanceof ModuleMetadata) { ModuleName moduleName = featureName.toModuleName(); ModuleTypeInfo moduleTypeInfoForFeature = owner.getPerspective().getMetaModule(moduleName).getTypeInfo(); CALDocComment caldoc = moduleTypeInfoForFeature.getCALDocComment(); buffer.append(getBasicMetadataHtml(owner, metadata, caldoc)); buffer.append(getModuleMetadataHtml(owner, (ModuleMetadata) metadata)); buffer.append(getAdditionalMetadataHtml(owner, metadata, caldoc)); } else if (metadata instanceof TypeClassMetadata) { QualifiedName qualifiedName = featureName.toQualifiedName(); ModuleTypeInfo moduleTypeInfoForFeature = owner.getPerspective().getMetaModule(qualifiedName.getModuleName()).getTypeInfo(); TypeClass typeClass = moduleTypeInfoForFeature.getTypeClass(qualifiedName.getUnqualifiedName()); CALDocComment caldoc = typeClass.getCALDocComment(); buffer.append(getBasicMetadataHtml(owner, metadata, caldoc)); buffer.append(getTypeClassMetadataHtml(owner, (TypeClassMetadata) metadata)); buffer.append(getAdditionalMetadataHtml(owner, metadata, caldoc)); } else if (metadata instanceof TypeConstructorMetadata) { QualifiedName qualifiedName = featureName.toQualifiedName(); ModuleTypeInfo moduleTypeInfoForFeature = owner.getPerspective().getMetaModule(qualifiedName.getModuleName()).getTypeInfo(); TypeConstructor typeCons = moduleTypeInfoForFeature.getTypeConstructor(qualifiedName.getUnqualifiedName()); CALDocComment caldoc = typeCons.getCALDocComment(); buffer.append(getBasicMetadataHtml(owner, metadata, caldoc)); buffer.append(getTypeConstructorMetadataHtml(owner, (TypeConstructorMetadata) metadata)); buffer.append(getAdditionalMetadataHtml(owner, metadata, caldoc)); } else if (metadata instanceof ClassInstanceMetadata) { ClassInstanceIdentifier classInstanceID = featureName.toInstanceIdentifier(); ModuleTypeInfo moduleTypeInfoForFeature = owner.getPerspective().getMetaModule(featureName.toModuleName()).getTypeInfo(); ClassInstance instance = moduleTypeInfoForFeature.getClassInstance(classInstanceID); CALDocComment caldoc = instance.getCALDocComment(); buffer.append(getBasicMetadataHtml(owner, metadata, caldoc)); buffer.append(getClassInstanceMetadataHtml(owner, (ClassInstanceMetadata) metadata)); buffer.append(getAdditionalMetadataHtml(owner, metadata, caldoc)); } else if (metadata instanceof InstanceMethodMetadata) { ClassInstanceIdentifier classInstanceID = featureName.toInstanceIdentifier(); ModuleTypeInfo moduleTypeInfoForFeature = owner.getPerspective().getMetaModule(featureName.toModuleName()).getTypeInfo(); ClassInstance instance = moduleTypeInfoForFeature.getClassInstance(classInstanceID); CALDocComment caldoc = instance.getMethodCALDocComment(featureName.toInstanceMethodName()); buffer.append(getBasicMetadataHtml(owner, metadata, caldoc)); buffer.append(getInstanceMethodMetadataHtml(owner, (InstanceMethodMetadata) metadata, url, caldoc)); buffer.append(getAdditionalMetadataHtml(owner, metadata, caldoc)); } } return buffer.toString(); } /** * Adds an appropriate icon to the HTML buffer. * @param buffer the buffer containing HTML. * @param url the NavAddress of the entity being documented. */ private static void addAppropriateIcon(final StringBuilder buffer, final NavAddress url) { final URL iconURL; if (url.getMethod() == NavAddress.MODULE_METHOD) { iconURL = GemCutter.class.getResource("/Resources/nav_module_big.png"); } else if (url.getMethod() == NavAddress.MODULE_NAMESPACE_METHOD) { iconURL = GemCutter.class.getResource("/Resources/nav_namespace_big.png"); } else { iconURL = null; } if (iconURL != null) { buffer.append("<img src='" + iconURL + "'>"); } } /** * @param owner the navigator owner * @param url the url of the page to get a header for * @return the HTML for the link table displayed at the top of metadata pages */ private static String getMetadataPageHeader(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); buffer.append(getPageLocationHtml(owner, url)); buffer.append("<table cellpadding='1' cellspacing='0' width='100%' bgcolor='#E0E0E0'><tr><td nowrap>"); buffer.append("<font face='arial' size='2'>"); CALFeatureMetadata metadata = NavAddressHelper.getMetadata(owner, url); // add links to the module vaults if (metadata instanceof ModuleMetadata) { ModuleName moduleName = ModuleName.make(url.getBase()); MetaModule module = owner.getPerspective().getMetaModule(moduleName); if (module.getTypeInfo().getNFunctions() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.FUNCTION_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Functions_Link"))); buffer.append(" | "); } if (module.getTypeInfo().getNTypeConstructors() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.TYPE_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Types_Link"))); buffer.append(" | "); } if (module.getTypeInfo().getNTypeClasses() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.CLASS_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Classes_Link"))); buffer.append(" | "); } if (module.getTypeInfo().getNClassInstances() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.INSTANCE_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Instances_Link"))); buffer.append(" | "); } } // add links to the anchors in the page in the same they appear in the page buffer.append(getLinkHtml(DESCRIPTION_ANCHOR, NavigatorMessages.getString("NAV_Description_Link"))); buffer.append(" | "); if (metadata instanceof FunctionalAgentMetadata || metadata instanceof InstanceMethodMetadata) { buffer.append(getLinkHtml(RESULT_ANCHOR, NavigatorMessages.getString("NAV_ReturnValue_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(ARGUMENTS_ANCHOR, NavigatorMessages.getString("NAV_Arguments_Link"))); buffer.append(" | "); if (metadata instanceof ClassMethodMetadata) { buffer.append(getLinkHtml(REQUIRED_METHOD_ANCHOR, NavigatorMessages.getString("NAV_RequiredMethod_Link"))); buffer.append(" | "); } buffer.append(getLinkHtml(EXAMPLES_ANCHOR, NavigatorMessages.getString("NAV_Examples_Link"))); buffer.append(" | "); } else if (metadata instanceof ModuleMetadata) { buffer.append(getLinkHtml(IMPORTS_ANCHOR, NavigatorMessages.getString("NAV_Imports_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(FRIENDS_ANCHOR, NavigatorMessages.getString("NAV_Friends_Link"))); buffer.append(" | "); } else if (metadata instanceof TypeClassMetadata) { buffer.append(getLinkHtml(PARENTS_ANCHOR, NavigatorMessages.getString("NAV_Parents_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(METHODS_ANCHOR, NavigatorMessages.getString("NAV_Methods_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(TYPES_ANCHOR, NavigatorMessages.getString("NAV_Types_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(INSTANCES_ANCHOR, NavigatorMessages.getString("NAV_Instances_Link"))); buffer.append(" | "); } else if (metadata instanceof TypeConstructorMetadata) { buffer.append(getLinkHtml(CONSTRUCTORS_ANCHOR, NavigatorMessages.getString("NAV_Constructors_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(CLASSES_ANCHOR, NavigatorMessages.getString("NAV_Classes_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(INSTANCES_ANCHOR, NavigatorMessages.getString("NAV_Instances_Link"))); buffer.append(" | "); } else if (metadata instanceof ClassInstanceMetadata) { buffer.append(getLinkHtml(INSTANCE_CLASS_ANCHOR, NavigatorMessages.getString("NAV_InstanceClass_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(INSTANCE_TYPE_ANCHOR, NavigatorMessages.getString("NAV_InstanceType_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(INSTANCE_METHODS_ANCHOR, NavigatorMessages.getString("NAV_InstanceMethods_Link"))); buffer.append(" | "); } buffer.append(getLinkHtml(GENERAL_ANCHOR, NavigatorMessages.getString("NAV_General_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(RELATED_ANCHOR, NavigatorMessages.getString("NAV_Related_Link"))); buffer.append(" | "); buffer.append(getLinkHtml(CUSTOM_ATTRIBUTES_ANCHOR, NavigatorMessages.getString("NAV_CustomAttributes_Link"))); buffer.append("</font>"); buffer.append("</td></tr></table>"); return buffer.toString(); } /** * @param url the url to display the not found page for * @return the HTML for the collector not found page */ private static String getCollectorNotFoundPage(NavAddress url) { StringBuilder buffer = new StringBuilder(); buffer.append(getHeaderHtml()); buffer.append("<h1>" + NavigatorMessages.getString("NAV_CollectorNotFound_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_CollectorNotFound_Message", url.getBase())); buffer.append(getFooterHtml()); return buffer.toString(); } /** * @param url the url to display the error page for * @return the HTML for the error page */ private static String getErrorPage(NavAddress url) { StringBuilder buffer = new StringBuilder(); buffer.append(getHeaderHtml()); buffer.append("<h1>" + NavigatorMessages.getString("NAV_ErrorDisplayingPage_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_ErrorDisplayingPage_Message", url.toString())); buffer.append(getFooterHtml()); return buffer.toString(); } /** * @param owner the navigator owner * @return the HTML for the workspace page */ private static String getWorkspacePage(NavFrameOwner owner) { StringBuilder buffer = new StringBuilder(); buffer.append(getHeaderHtml()); buffer.append("<h1>" + NavigatorMessages.getString("NAV_PropertiesBrowser_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_PropertiesBrowser_Message")); buffer.append("<h2>" + NavigatorMessages.getString("NAV_AvailableModules_Header") + "</h2>"); buffer.append(getModuleListHtml(owner)); buffer.append(getFooterHtml()); return buffer.toString(); } /** * @param owner the navigator owner * @return the HTML for the namespace page */ private static String getNamespacePage(NavFrameOwner owner, NavAddress url, ModuleName moduleName) { StringBuilder buffer = new StringBuilder(); buffer.append(getHeaderHtml()); final String title = moduleName.toSourceText(); generateTitleWithIcon(buffer, url, title); buffer.append("<h2>" + NavigatorMessages.getString("NAV_AvailableModules_Header") + "</h2>"); buffer.append(getModuleListInNamespaceHtml(owner, moduleName)); buffer.append(getFooterHtml()); return buffer.toString(); } /** * Generates a title with an icon. * @param buffer the HTML buffer. * @param url the NavAddress of the entity documented. * @param title the title. */ private static void generateTitleWithIcon(StringBuilder buffer, NavAddress url, final String title) { buffer.append("<table border=0 cellpadding=0 cellspacing=0 width=100%><tr><td valign='top'><h1>" + title + "</h1><td align='right'>"); addAppropriateIcon(buffer, url); buffer.append("</tr></table>"); } /** * @param owner the navigator owner * @param url the url to display the search page for * @return the HTML for the search page */ private static String getSearchPage(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); buffer.append(getHeaderHtml()); buffer.append(getSearchResultHtml(owner, url)); buffer.append(getFooterHtml()); return buffer.toString(); } /** * @param owner the navigator owner * @param url the url to display the vault page for * @return the HTML for the vault page */ private static String getVaultPage(NavFrameOwner owner, NavAddress url) { String vault = url.getParameter(NavAddress.VAULT_PARAMETER); String vaultHtml = null; if (vault.equals(NavAddress.CLASS_VAULT_VALUE)) { vaultHtml = getClassVaultHtml(owner, url); } else if (vault.equals(NavAddress.FUNCTION_VAULT_VALUE)) { vaultHtml = getFunctionVaultHtml(owner, url); } else if (vault.equals(NavAddress.INSTANCE_VAULT_VALUE)) { vaultHtml = getInstanceVaultHtml(owner, url); } else if (vault.equals(NavAddress.TYPE_VAULT_VALUE)) { vaultHtml = getTypeVaultHtml(owner, url); } if (vaultHtml == null) { return getErrorPage(url); } else { StringBuilder buffer = new StringBuilder(); buffer.append(getHeaderHtml()); buffer.append(getVaultPageHeader(owner, url)); buffer.append(vaultHtml); buffer.append(getFooterHtml()); return buffer.toString(); } } /** * @param owner the navigator owner * @param url the url to get a vault page header for * @return the HTML for a vault page header */ private static String getVaultPageHeader(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); buffer.append(getPageLocationHtml(owner, url)); buffer.append("<table cellpadding='1' cellspacing='0' width='100%' bgcolor='#E0E0E0'><tr><td nowrap>"); buffer.append("<font face='arial' size='2'>"); ModuleName moduleName = ModuleName.make(url.getBase()); MetaModule module = owner.getPerspective().getMetaModule(moduleName); url = url.withAllStripped(); if (module.getTypeInfo().getNFunctions() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.FUNCTION_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Functions_Link"))); buffer.append(" | "); } if (module.getTypeInfo().getNTypeConstructors() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.TYPE_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Types_Link"))); buffer.append(" | "); } if (module.getTypeInfo().getNTypeClasses() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.CLASS_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Classes_Link"))); buffer.append(" | "); } if (module.getTypeInfo().getNClassInstances() > 0) { NavAddress vaultUrl = url.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.INSTANCE_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_Instances_Link"))); buffer.append(" | "); } // strip trailing pipe buffer.delete(buffer.length() - 3, buffer.length()); buffer.append("</font>"); buffer.append("</td></tr></table>"); return buffer.toString(); } /** * @param owner the navigator owner * @return the HTML code for a hyperlinked list of modules from the workspace */ private static String getModuleListHtml(NavFrameOwner owner) { return getModuleListInNamespaceHtml(owner, null); } /** * @param owner the navigator owner * @param namespaceModuleName the namespace module name, or null if all modules are to be listed. * @return the HTML code for a hyperlinked list of modules from the workspace */ private static String getModuleListInNamespaceHtml(final NavFrameOwner owner, final ModuleName namespaceModuleName) { // get a sorted list of all modules List<MetaModule> modules = owner.getPerspective().getVisibleMetaModules(); modules.addAll(owner.getPerspective().getInvisibleMetaModules()); Collections.sort(modules, new MetaModuleComparator()); StringBuilder buffer = new StringBuilder(); buffer.append("<tt>"); for (final MetaModule metaModule : modules) { if (namespaceModuleName == null || namespaceModuleName.isProperPrefixOf(metaModule.getName())) { NavAddress moduleUrl = NavAddress.getAddress(metaModule); buffer.append(getLinkHtml(moduleUrl, NavAddressHelper.getDisplayText(owner, moduleUrl))); buffer.append(", "); } } // delete trailing comma buffer.delete(buffer.length() - 2, buffer.length()); buffer.append("</tt>"); return buffer.toString(); } /** * @param owner the navigator owner * @param url the url for the search results * @return the HTML for displaying the search results the URL points to */ private static String getSearchResultHtml(NavFrameOwner owner, NavAddress url) { // group the results by type String searchString = url.getBase(); List<NavAddress> results = owner.searchMetadata(searchString); List<NavAddress> functions = new ArrayList<NavAddress>(); List<NavAddress> types = new ArrayList<NavAddress>(); List<NavAddress> classes = new ArrayList<NavAddress>(); List<NavAddress> instances = new ArrayList<NavAddress>(); List<NavAddress> modules = new ArrayList<NavAddress>(); for (final NavAddress result : results) { if (result.getMethod() == NavAddress.FUNCTION_METHOD) { functions.add(result); } else if (result.getMethod() == NavAddress.TYPE_CONSTRUCTOR_METHOD) { types.add(result); } else if (result.getMethod() == NavAddress.TYPE_CLASS_METHOD) { classes.add(result); } else if (result.getMethod() == NavAddress.CLASS_INSTANCE_METHOD) { instances.add(result); } else if (result.getMethod() == NavAddress.MODULE_METHOD) { modules.add(result); } } // sort the results Comparator<NavAddress> urlSorter = new NavAddressComparator(owner); Collections.sort(functions, urlSorter); Collections.sort(types, urlSorter); Collections.sort(classes, urlSorter); Collections.sort(instances, urlSorter); Collections.sort(modules, urlSorter); // add the top bar with links to the anchors StringBuilder buffer = new StringBuilder(); buffer.append(getPageLocationHtml(owner, url)); if (results.size() > 0) { buffer.append("<table cellpadding='1' cellspacing='0' width='100%' bgcolor='#E0E0E0'><tr><td nowrap>"); buffer.append("<font face='arial' size='2'>"); if (functions.size() > 0) { buffer.append(getLinkHtml(FUNCTIONS_ANCHOR, NavigatorMessages.getString("NAV_Functions_Link"))); buffer.append(" | "); } if (types.size() > 0) { buffer.append(getLinkHtml(TYPES_ANCHOR, NavigatorMessages.getString("NAV_Types_Link"))); buffer.append(" | "); } if (classes.size() > 0) { buffer.append(getLinkHtml(CLASSES_ANCHOR, NavigatorMessages.getString("NAV_Classes_Link"))); buffer.append(" | "); } if (instances.size() > 0) { buffer.append(getLinkHtml(INSTANCES_ANCHOR, NavigatorMessages.getString("NAV_Instances_Link"))); buffer.append(" | "); } if (modules.size() > 0) { buffer.append(getLinkHtml(MODULES_ANCHOR, NavigatorMessages.getString("NAV_Modules_Link"))); buffer.append(" | "); } // remove trailing pipe buffer.delete(buffer.length() - 3, buffer.length()); buffer.append("</font>"); buffer.append("</td></tr></table>"); } // format the message that indicates the number of search results String message = NavigatorMessages.getString("NAV_SearchResults_Message"); String[] choices = { NavigatorMessages.getString("NAV_SearchResultsNone"), NavigatorMessages.getString("NAV_SearchResultsOne"), NavigatorMessages.getString("NAV_SearchResultsMultiple") }; double[] limits = { 0, 1, 2 }; Format[] formats = { new ChoiceFormat(limits, choices), null }; Object[] arguments = { Integer.valueOf(results.size()), url.getBase() }; MessageFormat messageFormat = new MessageFormat(message); messageFormat.setFormats(formats); // now print the message and put the search results below it buffer.append("<h1>" + NavigatorMessages.getString("NAV_SearchResults_Header") + "</h1>"); buffer.append(messageFormat.format(arguments)); Pattern searchPattern = Pattern.compile(WildcardPatternMatcher.wildcardPatternToRegExp(searchString), Pattern.CASE_INSENSITIVE); if (functions.size() > 0) { buffer.append("<h2>" + getAnchorHtml(FUNCTIONS_ANCHOR, NavigatorMessages.getString("NAV_Functions_Header")) + "</h2>"); for (final NavAddress result : functions) { buffer.append(getLinkHtml(result, getMatchesHighlightedHtml(searchPattern, NavAddressHelper.getDisplayText(owner, result)))); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 3, buffer.length()); } if (types.size() > 0) { buffer.append("<h2>" + getAnchorHtml(TYPES_ANCHOR, NavigatorMessages.getString("NAV_Types_Header")) + "</h2>"); for (final NavAddress result : types) { buffer.append(getLinkHtml(result, getMatchesHighlightedHtml(searchPattern, NavAddressHelper.getDisplayText(owner, result)))); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 3, buffer.length()); } if (classes.size() > 0) { buffer.append("<h2>" + getAnchorHtml(CLASSES_ANCHOR, NavigatorMessages.getString("NAV_Classes_Header")) + "</h2>"); for (final NavAddress result : classes) { buffer.append(getLinkHtml(result, getMatchesHighlightedHtml(searchPattern, NavAddressHelper.getDisplayText(owner, result)))); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 3, buffer.length()); } if (instances.size() > 0) { buffer.append("<h2>" + getAnchorHtml(INSTANCES_ANCHOR, NavigatorMessages.getString("NAV_Instances_Header")) + "</h2>"); for (final NavAddress result : instances) { buffer.append(getLinkHtml(result, getMatchesHighlightedHtml(searchPattern, NavAddressHelper.getDisplayText(owner, result)))); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 3, buffer.length()); } if (modules.size() > 0) { buffer.append("<h2>" + getAnchorHtml(MODULES_ANCHOR, NavigatorMessages.getString("NAV_Modules_Header")) + "</h2>"); for (final NavAddress result : modules) { buffer.append(getLinkHtml(result, getMatchesHighlightedHtml(searchPattern, NavAddressHelper.getDisplayText(owner, result)))); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 3, buffer.length()); } return buffer.toString(); } /** * @param searchPattern the regexp search pattern * @param text the text containing the matches to be highlighted * @return the HTML code for the text, including the appropriate highlighting tags */ private static String getMatchesHighlightedHtml(Pattern searchPattern, String text) { return searchPattern.matcher(text).replaceAll("<b>$0</b>"); } /** * @param owner the navigator owner * @param url the module address to display the vault for * @return the HTML code for the module function vault */ private static String getFunctionVaultHtml(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); String moduleLink = getLinkHtml(url.withAllStripped(), NavAddressHelper.getDisplayText(owner, url.withAllStripped())); buffer.append("<h1>" + NavigatorMessages.getString("NAV_FunctionVault_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_FunctionVault_Message", moduleLink)); buffer.append("<br><br><tt>"); ModuleName moduleName = ModuleName.make(url.getBase()); MetaModule module = owner.getPerspective().getMetaModule(moduleName); SortedSet<ScopedEntity> entities = new TreeSet<ScopedEntity>(new ScopedEntityComparator()); int count = module.getTypeInfo().getNFunctions(); for (int n = 0; n < count; n++) { entities.add(module.getTypeInfo().getNthFunction(n)); } for (final ScopedEntity entity : entities) { NavAddress entityAddress = NavAddress.getAddress(entity); buffer.append(getLinkHtml(entityAddress, NavAddressHelper.getDisplayText(owner, entityAddress, ScopedEntityNamingPolicy.UNQUALIFIED))); buffer.append(", "); } buffer.delete(buffer.length() - 2, buffer.length()); // stip trailing comma buffer.append("</tt>"); return buffer.toString(); } /** * @param owner the navigator owner * @param url the module address to display the vault for * @return the HTML code for the module class vault */ private static String getClassVaultHtml(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); String moduleLink = getLinkHtml(url.withAllStripped(), NavAddressHelper.getDisplayText(owner, url.withAllStripped())); buffer.append("<h1>" + NavigatorMessages.getString("NAV_ClassVault_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_ClassVault_Message", moduleLink)); buffer.append("<br><br><tt>"); ModuleName moduleName = ModuleName.make(url.getBase()); MetaModule module = owner.getPerspective().getMetaModule(moduleName); SortedSet<TypeClass> entities = new TreeSet<TypeClass>(new ScopedEntityComparator()); int count = module.getTypeInfo().getNTypeClasses(); for (int n = 0; n < count; n++) { entities.add(module.getTypeInfo().getNthTypeClass(n)); } for (final ScopedEntity entity : entities) { NavAddress entityAddress = NavAddress.getAddress(entity); buffer.append(getLinkHtml(entityAddress, NavAddressHelper.getDisplayText(owner, entityAddress, ScopedEntityNamingPolicy.UNQUALIFIED))); buffer.append(", "); } buffer.delete(buffer.length() - 2, buffer.length()); // stip trailing comma buffer.append("</tt>"); return buffer.toString(); } /** * @param owner the navigator owner * @param url the module address to display the vault for * @return the HTML code for the module type vault */ private static String getTypeVaultHtml(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); String moduleLink = getLinkHtml(url.withAllStripped(), NavAddressHelper.getDisplayText(owner, url.withAllStripped())); buffer.append("<h1>" + NavigatorMessages.getString("NAV_TypeVault_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_TypeVault_Message", moduleLink)); buffer.append("<br><br><tt>"); ModuleName moduleName = ModuleName.make(url.getBase()); MetaModule module = owner.getPerspective().getMetaModule(moduleName); SortedSet<TypeConstructor> entities = new TreeSet<TypeConstructor>(new ScopedEntityComparator()); int count = module.getTypeInfo().getNTypeConstructors(); for (int n = 0; n < count; n++) { entities.add(module.getTypeInfo().getNthTypeConstructor(n)); } for (final ScopedEntity entity : entities) { NavAddress entityAddress = NavAddress.getAddress(entity); buffer.append(getLinkHtml(entityAddress, NavAddressHelper.getDisplayText(owner, entityAddress, ScopedEntityNamingPolicy.UNQUALIFIED))); buffer.append(", "); } buffer.delete(buffer.length() - 2, buffer.length()); // stip trailing comma buffer.append("</tt>"); return buffer.toString(); } /** * @param owner the navigator owner * @param url the module address to display the vault for * @return the HTML code for the module instance vault */ private static String getInstanceVaultHtml(NavFrameOwner owner, NavAddress url) { StringBuilder buffer = new StringBuilder(); String moduleLink = getLinkHtml(url.withAllStripped(), NavAddressHelper.getDisplayText(owner, url.withAllStripped())); buffer.append("<h1>" + NavigatorMessages.getString("NAV_InstanceVault_Header") + "</h1>"); buffer.append(NavigatorMessages.getString("NAV_InstanceVault_Message", moduleLink)); buffer.append("<br><br><tt>"); ModuleName moduleName = ModuleName.make(url.getBase()); MetaModule module = owner.getPerspective().getMetaModule(moduleName); SortedSet<ClassInstance> instances = new TreeSet<ClassInstance>(new ClassInstanceComparator()); int count = module.getTypeInfo().getNClassInstances(); for (int n = 0; n < count; n++) { instances.add(module.getTypeInfo().getNthClassInstance(n)); } ScopedEntityNamingPolicy namingPolicy = new UnqualifiedUnlessAmbiguous(module.getTypeInfo()); for (final ClassInstance instance : instances) { NavAddress address = NavAddress.getAddress(instance); buffer.append("  " + getLinkHtml(address, instance.getNameWithContext(namingPolicy))); buffer.append("<br>"); } buffer.append("</tt>"); return buffer.toString(); } /** * @param owner the navigator owner * @param metadata the metadata to get basic HTML for * @param caldoc the associated CALDoc comment * @return the HTML for the basic metadata information */ private static String getBasicMetadataHtml(NavFrameOwner owner, CALFeatureMetadata metadata, CALDocComment caldoc) { StringBuilder buffer = new StringBuilder(); // Display the description for the gem. buffer.append("<h2>" + getAnchorHtml(DESCRIPTION_ANCHOR, NavigatorMessages.getString("NAV_Description_Header")) + "</h2>"); if (metadata.getLongDescription() != null) { buffer.append(metadata.getLongDescription()); } else if (metadata.getShortDescription() != null) { buffer.append(metadata.getShortDescription()); } else { if (caldoc != null) { String caldocHtml = getHtmlForCALDocTextBlock(caldoc.getDescriptionBlock(), owner, false); if (caldocHtml.length() > 0) { buffer.append(caldocHtml); } else { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } } else { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } } return buffer.toString(); } /** * @param owner the navigator owner * @param metadata the metadata to get basic HTML for * @param caldoc the associated CALDoc comment * @return the HTML for additional metadata information */ private static String getAdditionalMetadataHtml(NavFrameOwner owner, CALFeatureMetadata metadata, CALDocComment caldoc) { StringBuilder buffer = new StringBuilder(); // Add a General section buffer.append("<h2>" + getAnchorHtml(GENERAL_ANCHOR, NavigatorMessages.getString("NAV_General_Header")) + "</h2>"); buffer.append("<table border='0'>"); buffer.append("<tr><td valign='top'>" + NavigatorMessages.getString("NAV_CreationDate") + "</td><td>"); Date creationDate = metadata.getCreationDate(); if (creationDate == null) { buffer.append(NavigatorMessages.getString("NAV_UnknownValue")); } else { buffer.append(LOCALE_DATE_FORMAT.format(creationDate)); } buffer.append("</td></tr><tr><td valign='top'>" + NavigatorMessages.getString("NAV_ModificationDate") + "</td><td>"); Date modificationDate = metadata.getModificationDate(); if (modificationDate == null) { buffer.append(NavigatorMessages.getString("NAV_UnknownValue")); } else { buffer.append(LOCALE_DATE_FORMAT.format(modificationDate)); } buffer.append("</td></tr><tr><td valign='top'>" + NavigatorMessages.getString("NAV_Author") + "</td><td>"); String author = metadata.getAuthor(); if (author == null || author.length() == 0) { // default to display the information in the CALDoc @author tags boolean seenAtLeastOneAuthorTag = false; if (caldoc != null) { for (int i = 0, nAuthors = caldoc.getNAuthorBlocks(); i < nAuthors; i++) { CALDocComment.TextBlock block = caldoc.getNthAuthorBlock(i); buffer.append(getHtmlForCALDocTextBlock(block, owner, false)); if (i < nAuthors - 1) { buffer.append("<br>"); } seenAtLeastOneAuthorTag = true; } } if (!seenAtLeastOneAuthorTag) { buffer.append(NavigatorMessages.getString("NAV_UnknownValue")); } } else { buffer.append(author); } buffer.append("</td></tr><tr><td valign='top'>" + NavigatorMessages.getString("NAV_Version") + "</td><td>"); String version = metadata.getVersion(); if (version == null || version.length() == 0) { // default to display the information in the CALDoc @version tag boolean seenVersionTag = false; if (caldoc != null) { CALDocComment.TextBlock versionBlock = caldoc.getVersionBlock(); if (versionBlock != null) { buffer.append(getHtmlForCALDocTextBlock(versionBlock, owner, false)); seenVersionTag = true; } } if (!seenVersionTag) { buffer.append(NavigatorMessages.getString("NAV_UnknownValue")); } } else { buffer.append(version); } if (metadata instanceof ScopedEntityMetadata) { CALFeatureName featureName = ((ScopedEntityMetadata) metadata).getFeatureName(); ScopedEntity entity = owner.getPerspective().getWorkspace().getScopedEntity(featureName); if (entity != null) { buffer.append("</td></tr><tr><td valign='top'>" + NavigatorMessages.getString("NAV_Visibility") + "</td><td>"); buffer.append(entity.getScope().toString()); } } if (metadata instanceof FunctionalAgentMetadata) { buffer.append("</td></tr><tr><td valign='top'>" + NavigatorMessages.getString("NAV_Categories") + "</td><td>"); String[] categories = ((FunctionalAgentMetadata) metadata).getCategories(); if (categories.length == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { for (final String category : categories) { buffer.append(category); buffer.append(", "); } // remove trailing comma buffer.delete(buffer.length() - 2, buffer.length()); } } buffer.append("</td></tr></table>"); // Put in a section for the related features. buffer.append("<h2>" + getAnchorHtml(RELATED_ANCHOR, NavigatorMessages.getString("NAV_RelatedFeatures_Header")) + "</h2>"); int featureCount = metadata.getNRelatedFeatures(); if (featureCount == 0) { if (caldoc != null) { // add the CALDoc @see references if the metadata specifies no related features boolean seenSeeBlock = false; for (int i = 0, nModuleRefs = caldoc.getNModuleReferences(); i < nModuleRefs; i++) { seenSeeBlock = true; ModuleName name = caldoc.getNthModuleReference(i).getName(); CALFeatureName featureName = CALFeatureName.getModuleFeatureName(name); buffer.append(getRelatedFeatureLinkHtml(owner, featureName)).append("<br>"); } for (int i = 0, nFuncRefs = caldoc.getNFunctionOrClassMethodReferences(); i < nFuncRefs; i++) { seenSeeBlock = true; QualifiedName name = caldoc.getNthFunctionOrClassMethodReference(i).getName(); CALFeatureName featureName; if (isNameForClassMethod(owner, name)) { featureName = CALFeatureName.getClassMethodFeatureName(name); } else { featureName = CALFeatureName.getFunctionFeatureName(name); } buffer.append(getRelatedFeatureLinkHtml(owner, featureName)).append("<br>"); } for (int i = 0, nTypeConsRefs = caldoc.getNTypeConstructorReferences(); i < nTypeConsRefs; i++) { seenSeeBlock = true; QualifiedName name = caldoc.getNthTypeConstructorReference(i).getName(); CALFeatureName featureName = CALFeatureName.getTypeConstructorFeatureName(name); buffer.append(getRelatedFeatureLinkHtml(owner, featureName)).append("<br>"); } for (int i = 0, nDataConsRefs = caldoc.getNDataConstructorReferences(); i < nDataConsRefs; i++) { seenSeeBlock = true; QualifiedName name = caldoc.getNthDataConstructorReference(i).getName(); CALFeatureName featureName = CALFeatureName.getDataConstructorFeatureName(name); buffer.append(getRelatedFeatureLinkHtml(owner, featureName)).append("<br>"); } for (int i = 0, nTypeClassRefs = caldoc.getNTypeClassReferences(); i < nTypeClassRefs; i++) { seenSeeBlock = true; QualifiedName name = caldoc.getNthTypeClassReference(i).getName(); CALFeatureName featureName = CALFeatureName.getTypeClassFeatureName(name); buffer.append(getRelatedFeatureLinkHtml(owner, featureName)).append("<br>"); } if (seenSeeBlock) { // remove trailing <br> buffer.delete(buffer.length() - 4, buffer.length()); } else { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } } else { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } } else { for (int i = 0; i < featureCount; i++) { buffer.append(getRelatedFeatureLinkHtml(owner, metadata.getNthRelatedFeature(i))).append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 4, buffer.length()); } // Put in a section for the custom attributes. buffer.append("<h2>" + getAnchorHtml(CUSTOM_ATTRIBUTES_ANCHOR, NavigatorMessages.getString("NAV_CustomAttributes_Header")) + "</h2>"); Iterator<String> it = metadata.getAttributeNames(); if (it.hasNext()) { buffer.append("<table border='0'>"); while (it.hasNext()) { String attributeName = it.next(); String attributeValue = metadata.getAttribute(attributeName); buffer.append("<tr><td valign='top'>"); buffer.append(attributeName); buffer.append(NavigatorMessages.getString("NAV_CustomAttributeNameValueSeparator")); buffer.append("</td><td valign='top'>"); if (attributeValue != null) { buffer.append(attributeValue); } else { buffer.append(NavigatorMessages.getString("NAV_UnknownValue")); } buffer.append("</td></tr>"); } buffer.append("</table>"); } else { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } return buffer.toString(); } /** * @param owner the navigator owner. * @param featureName the feature name of the related feature. * @return the HTML for a link to the related feature's page, for a disabled link if the related feature has no metadata. */ private static String getRelatedFeatureLinkHtml(NavFrameOwner owner, CALFeatureName featureName) { NavAddress address = NavAddress.getAddress(featureName); String displayName = NavAddressHelper.getDisplayText(owner, address); return getRelatedFeatureLinkHtml(owner, address, displayName); } /** * @param owner the navigator owner. * @param featureName the feature name of the related feature. * @param moduleNameInSource how the module name portion of the reference appears in source. Could be the empty string if the reference is unqualified in source. * @return the HTML for a link to the related feature's page, for a disabled link if the related feature has no metadata. */ private static String getRelatedFeatureLinkHtml(NavFrameOwner owner, CALFeatureName featureName, String moduleNameInSource) { NavAddress address = NavAddress.getAddress(featureName); String displayName; if (featureName.isScopedEntityName()) { String unqualifiedName = featureName.toQualifiedName().getUnqualifiedName(); if (moduleNameInSource.length() == 0) { displayName = unqualifiedName; } else { displayName = moduleNameInSource + '.' + unqualifiedName; } } else if (featureName.getType() == CALFeatureName.MODULE && moduleNameInSource.length() > 0) { displayName = moduleNameInSource; } else { displayName = NavAddressHelper.getDisplayText(owner, address); } return getRelatedFeatureLinkHtml(owner, address, displayName); } /** * @param owner the navigator owner. * @param address the address of the feature to link to. * @param displayName the name to be displayed for the link. * @return the HTML for a link to the related feature's page, for a disabled link if the related feature has no metadata. */ private static String getRelatedFeatureLinkHtml(NavFrameOwner owner, NavAddress address, String displayName) { // If there is metadata for a feature, then provide a link to it. // Otherwise, show the name of the feature in a disabled style. CALFeatureMetadata featureMetadata = NavAddressHelper.getMetadata(owner, address); if (featureMetadata != null) { return getLinkHtml(address, displayName); } else { return getDisabledHtml(displayName); } } /** * @param owner the navigator owner * @param metadata the metadata to get HTML for * @return the HTML for module metadata specific information */ private static String getModuleMetadataHtml(NavFrameOwner owner, ModuleMetadata metadata) { StringBuilder buffer = new StringBuilder(); // create a list of imported modules buffer.append("<h2>" + getAnchorHtml(IMPORTS_ANCHOR, NavigatorMessages.getString("NAV_ImportedModules_Header")) + "</h2>"); SortedSet<ModuleTypeInfo> importedModules = new TreeSet<ModuleTypeInfo>(new ModuleTypeInfoComparator()); ModuleName moduleName = metadata.getFeatureName().toModuleName(); ModuleTypeInfo moduleTypeInfo = owner.getPerspective().getMetaModule(moduleName).getTypeInfo(); int nImportedModules = moduleTypeInfo.getNImportedModules(); if (nImportedModules == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { for (int n = 0; n < nImportedModules; n++) { importedModules.add(moduleTypeInfo.getNthImportedModule(n)); } buffer.append("<tt>"); for (final ModuleTypeInfo importedModuleInfo : importedModules) { NavAddress moduleAddress = NavAddress.getAddress(importedModuleInfo); buffer.append(getLinkHtml(moduleAddress, NavAddressHelper.getDisplayText(owner, moduleAddress))); buffer.append(", "); } // remove trailing comma buffer.delete(buffer.length() - 2, buffer.length()); buffer.append("</tt>"); } // create a list of friend modules buffer.append("<h2>" + getAnchorHtml(FRIENDS_ANCHOR, NavigatorMessages.getString("NAV_FriendModules_Header")) + "</h2>"); SortedSet<ModuleName> friendModules = new TreeSet<ModuleName>(); int nFriendModules = moduleTypeInfo.getNFriendModules(); if (nFriendModules == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { for (int n = 0; n < nFriendModules; n++) { friendModules.add(moduleTypeInfo.getNthFriendModule(n)); } buffer.append("<tt>"); for (final ModuleName friendModule : friendModules) { NavAddress moduleAddress = NavAddress.getAddress(CALFeatureName.getModuleFeatureName(friendModule)); String displayText = NavAddressHelper.getDisplayText(owner, moduleAddress); CALFeatureMetadata friendMetadata = NavAddressHelper.getMetadata(owner, moduleAddress); if (friendMetadata != null) { buffer.append(getLinkHtml(moduleAddress, displayText)); } else { buffer.append(getDisabledHtml(displayText)); } buffer.append(", "); } // remove trailing comma buffer.delete(buffer.length() - 2, buffer.length()); buffer.append("</tt>"); } return buffer.toString(); } /** * @param owner the navigator owner * @param metadata the metadata to get HTML for * @return the HTML for type constructor metadata specific information */ private static String getTypeConstructorMetadataHtml(NavFrameOwner owner, TypeConstructorMetadata metadata) { TypeConstructor typeCons = (TypeConstructor) owner.getPerspective().getWorkspace().getScopedEntity(metadata.getFeatureName()); StringBuilder buffer = new StringBuilder(); ScopedEntityNamingPolicy namingPolicy = new UnqualifiedUnlessAmbiguous(owner.getPerspective().getWorkingModuleTypeInfo()); // list the data constructors for this type buffer.append("<h2>" + getAnchorHtml(CONSTRUCTORS_ANCHOR, NavigatorMessages.getString("NAV_Constructors_Header")) + "</h2>"); int count = typeCons.getNDataConstructors(); if (count == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { buffer.append("<tt>"); for (int i = 0; i < count; i++) { DataConstructor dataCons = typeCons.getNthDataConstructor(i); NavAddress dataConsUrl = NavAddress.getAddress(dataCons); String dataConsLink = getLinkHtml(dataConsUrl, NavAddressHelper.getDisplayText(owner, dataConsUrl, ScopedEntityNamingPolicy.UNQUALIFIED)); buffer.append("<b>" + dataConsLink + "</b> :: "); buffer.append("<i>" + getTypeStringHtml(owner, dataCons.getTypeExpr(), namingPolicy) + "</i>"); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 4, buffer.length()); buffer.append("</tt>"); } // we have to search all instances in all modules to see if they're an instance of this type //todoBI the above comment is false. Need to just search all module imported, either directly or indirectly, //into the module in which the instance is defined. CALWorkspace workspace = owner.getPerspective().getWorkspace(); List<TypeClass> classes = new ArrayList<TypeClass>(); List<ClassInstance> instances = new ArrayList<ClassInstance>(); for (int n = 0, numMods = workspace.getNMetaModules(); n < numMods; n++) { MetaModule metaModule = workspace.getNthMetaModule(n); ModuleTypeInfo moduleTypeInfo = metaModule.getTypeInfo(); for (int i = 0, num = moduleTypeInfo.getNClassInstances(); i < num; i++) { ClassInstance instance = moduleTypeInfo.getNthClassInstance(i); if (instance.isTypeConstructorInstance() && ((ClassInstanceIdentifier.TypeConstructorInstance)instance.getIdentifier()).getTypeConsName().equals(typeCons.getName())) { classes.add(instance.getTypeClass()); instances.add(instance); } } } // list the implemented classes for this type buffer.append("<h2>" + getAnchorHtml(CLASSES_ANCHOR, NavigatorMessages.getString("NAV_InstanceClasses_Header")) + "</h2>"); if (classes.isEmpty()) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { Collections.sort(classes, new ScopedEntityComparator()); buffer.append("<tt>"); for (final TypeClass typeClass : classes) { NavAddress address = NavAddress.getAddress(typeClass); buffer.append(getLinkHtml(address, NavAddressHelper.getDisplayText(owner, address, namingPolicy))); buffer.append(", "); } buffer.delete(buffer.length() - 2, buffer.length()); // strip trailing comma buffer.append("</tt>"); } // list the implemented instances for this type buffer.append("<h2>" + getAnchorHtml(INSTANCES_ANCHOR, NavigatorMessages.getString("NAV_Instances_Header")) + "</h2>"); if (instances.isEmpty()) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { Collections.sort(instances, new ClassInstanceComparator()); buffer.append("<tt>"); for (final ClassInstance instance : instances) { NavAddress address = NavAddress.getAddress(instance); buffer.append("  "); buffer.append(getLinkHtml(address, NavAddressHelper.getDisplayText(owner, address, namingPolicy))); buffer.append("<br>"); } buffer.delete(buffer.length() - 4, buffer.length()); // strip trailing <br> buffer.append("</tt>"); } return buffer.toString(); } /** * @param owner the navigator owner * @param metadata the metadata to get HTML for * @return the HTML for type class metadata specific information */ private static String getTypeClassMetadataHtml(NavFrameOwner owner, TypeClassMetadata metadata) { TypeClass typeClass = (TypeClass) owner.getPerspective().getWorkspace().getScopedEntity(metadata.getFeatureName()); StringBuilder buffer = new StringBuilder(); ScopedEntityNamingPolicy namingPolicy = new UnqualifiedUnlessAmbiguous(owner.getPerspective().getWorkingModuleTypeInfo()); // list the parent classes of the class buffer.append("<h2>" + getAnchorHtml(PARENTS_ANCHOR, NavigatorMessages.getString("NAV_ParentClasses_Header")) + "</h2>"); int count = typeClass.getNParentClasses(); if (count == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { SortedSet<TypeClass> parents = new TreeSet<TypeClass>(new ScopedEntityQualifiedComparator()); for (int i = 0; i < count; i++) { parents.add(typeClass.getNthParentClass(i)); } buffer.append("<tt>"); for (final TypeClass parentClass : parents) { NavAddress parentUrl = NavAddress.getAddress(parentClass); buffer.append(getLinkHtml(parentUrl, NavAddressHelper.getDisplayText(owner, parentUrl, namingPolicy)) + ", "); } // remove trailing comma buffer.delete(buffer.length() - 2, buffer.length()); buffer.append("</tt>"); } // list the class methods of this class buffer.append("<h2>" + getAnchorHtml(METHODS_ANCHOR, NavigatorMessages.getString("NAV_ClassMethods_Header")) + "</h2>"); buffer.append("<tt>"); count = typeClass.getNClassMethods(); for (int i = 0; i < count; i++) { ClassMethod method = typeClass.getNthClassMethod(i); NavAddress methodUrl = NavAddress.getAddress(method); buffer.append("<b>" + getLinkHtml(methodUrl, NavAddressHelper.getDisplayText(owner, methodUrl, ScopedEntityNamingPolicy.UNQUALIFIED) + "</b> :: ")); buffer.append("<i>" + getTypeStringHtml(owner, method.getTypeExpr(), namingPolicy) + "</i>"); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 4, buffer.length()); buffer.append("</tt>"); // we have to search all instances in all modules to see if they're an instance of this class CALWorkspace workspace = owner.getPerspective().getWorkspace(); List<ClassInstance> instances = new ArrayList<ClassInstance>(); List<TypeConstructor> types = new ArrayList<TypeConstructor>(); for (int n = 0, numMods = workspace.getNMetaModules(); n < numMods; n++) { MetaModule metaModule = workspace.getNthMetaModule(n); ModuleTypeInfo moduleTypeInfo = metaModule.getTypeInfo(); for (int i = 0, num = moduleTypeInfo.getNClassInstances(); i < num; i++) { ClassInstance instance = moduleTypeInfo.getNthClassInstance(i); if (instance.isUniversalRecordInstance()) { continue; } if (instance.getTypeClass().getName().equals(typeClass.getName())) { QualifiedName typeConsName = ((TypeConsApp)instance.getType()).getName(); MetaModule typeModule = owner.getPerspective().getMetaModule(typeConsName.getModuleName()); TypeConstructor typeCons = typeModule.getTypeInfo().getTypeConstructor(typeConsName.getUnqualifiedName()); types.add(typeCons); instances.add(instance); } } } // list the instance types of this class buffer.append("<h2>" + getAnchorHtml(TYPES_ANCHOR, NavigatorMessages.getString("NAV_InstanceTypes_Header")) + "</h2>"); if (types.isEmpty()) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { Collections.sort(types, new ScopedEntityComparator()); buffer.append("<tt>"); for (final ScopedEntity entity : types) { NavAddress entityAddress = NavAddress.getAddress(entity); buffer.append(getLinkHtml(entityAddress, NavAddressHelper.getDisplayText(owner, entityAddress, namingPolicy))); buffer.append(", "); } buffer.delete(buffer.length() - 2, buffer.length()); // strip trailing comma buffer.append("</tt>"); } // list the instances of this class buffer.append("<h2>" + getAnchorHtml(INSTANCES_ANCHOR, NavigatorMessages.getString("NAV_Instances_Header")) + "</h2>"); if (instances.isEmpty()) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { Collections.sort(instances, new ClassInstanceComparator()); buffer.append("<tt>"); for (final ClassInstance instance : instances) { NavAddress instanceAddress = NavAddress.getAddress(instance); buffer.append("  "); buffer.append(getLinkHtml(instanceAddress, NavAddressHelper.getDisplayText(owner, instanceAddress, namingPolicy))); buffer.append("<br>"); } buffer.delete(buffer.length() - 4, buffer.length()); // strip trailing <br> buffer.append("</tt>"); } return buffer.toString(); } /** * @param owner the navigator owner * @param metadata the metadata to get HTML for * @param url the address of the page * @param caldoc the CALDoc comment associated with the instance method, or null if there is none * @return the HTML for entity metadata specific information */ private static String getInstanceMethodMetadataHtml(NavFrameOwner owner, InstanceMethodMetadata metadata, NavAddress url, CALDocComment caldoc) { return getFunctionalAgentOrInstanceMethodArgumentsAndReturnValueHtml(owner, metadata.getArguments(), metadata.getReturnValueDescription(), url, caldoc) + getFunctionalAgentOrInstanceMethodExamplesHtml(owner, metadata.getExamples()); } /** * @param owner the navigator owner * @param metadata the metadata to get HTML for * @return the HTML for class instance metadata specific information */ private static String getClassInstanceMetadataHtml(NavFrameOwner owner, ClassInstanceMetadata metadata) { StringBuilder buffer = new StringBuilder(); ScopedEntityNamingPolicy namingPolicy = new UnqualifiedUnlessAmbiguous(owner.getPerspective().getWorkingModuleTypeInfo()); ClassInstanceIdentifier identifier = metadata.getFeatureName().toInstanceIdentifier(); CALFeatureName className = CALFeatureName.getTypeClassFeatureName(identifier.getTypeClassName()); TypeClass typeClass = (TypeClass) owner.getPerspective().getWorkspace().getScopedEntity(className); // list the instance class and type buffer.append("<h2>" + getAnchorHtml(INSTANCE_CLASS_ANCHOR, NavigatorMessages.getString("NAV_InstanceClass_Header")) + "</h2>"); buffer.append(getLinkHtml(NavAddress.getAddress(typeClass), typeClass.getAdaptedName(namingPolicy))); if (identifier instanceof ClassInstanceIdentifier.TypeConstructorInstance) { CALFeatureName typeName = CALFeatureName.getTypeConstructorFeatureName(((ClassInstanceIdentifier.TypeConstructorInstance)identifier).getTypeConsName()); TypeConstructor typeCons = (TypeConstructor) owner.getPerspective().getWorkspace().getScopedEntity(typeName); buffer.append("<h2>" + getAnchorHtml(INSTANCE_TYPE_ANCHOR, NavigatorMessages.getString("NAV_InstanceType_Header")) + "</h2>"); buffer.append(getLinkHtml(NavAddress.getAddress(typeCons), typeCons.getAdaptedName(namingPolicy))); } // list the instance methods of this instance buffer.append("<h2>" + getAnchorHtml(INSTANCE_METHODS_ANCHOR, NavigatorMessages.getString("NAV_InstanceMethods_Header")) + "</h2>"); buffer.append("<tt>"); ModuleTypeInfo featureModuleTypeInfo = owner.getPerspective().getMetaModule(metadata.getFeatureName().toModuleName()).getTypeInfo(); ClassInstance instance = featureModuleTypeInfo.getClassInstance(identifier); int count = instance.getNInstanceMethods(); for (int i = 0; i < count; i++) { ClassMethod method = typeClass.getNthClassMethod(i); String methodName = method.getName().getUnqualifiedName(); NavAddress methodUrl = NavAddress.getAddress(CALFeatureName.getInstanceMethodFeatureName(identifier, metadata.getFeatureName().toModuleName(), methodName)); buffer.append("<b>" + getLinkHtml(methodUrl, NavAddressHelper.getDisplayText(owner, methodUrl, ScopedEntityNamingPolicy.UNQUALIFIED) + "</b> :: ")); buffer.append("<i>" + getTypeStringHtml(owner, instance.getInstanceMethodType(i), namingPolicy) + "</i>"); buffer.append("<br>"); } // remove trailing <br> buffer.delete(buffer.length() - 4, buffer.length()); buffer.append("</tt>"); return buffer.toString(); } /** * @param owner the navigator owner * @param metadata the metadata to get HTML for * @param url the address of the page * @param caldoc the CALDoc comment associated with the entity, or null if there is none * @param isRequiredClassMethod whether the documented entity is a required class method * @return the HTML for entity metadata specific information */ private static String getFunctionalAgentMetadataHtml(NavFrameOwner owner, FunctionalAgentMetadata metadata, NavAddress url, CALDocComment caldoc, boolean isRequiredClassMethod) { String returnValueDesc; StringBuilder buffer = new StringBuilder(); if (metadata instanceof FunctionMetadata) { returnValueDesc = ((FunctionMetadata)metadata).getReturnValueDescription(); buffer.append(getFunctionalAgentOrInstanceMethodArgumentsAndReturnValueHtml(owner, metadata.getArguments(), returnValueDesc, url, caldoc)); } else if (metadata instanceof ClassMethodMetadata) { returnValueDesc = ((ClassMethodMetadata)metadata).getReturnValueDescription(); buffer.append(getFunctionalAgentOrInstanceMethodArgumentsAndReturnValueHtml(owner, metadata.getArguments(), returnValueDesc, url, caldoc)); // for class method we add the display of whether it is a required method buffer.append("<h2>").append(getAnchorHtml(REQUIRED_METHOD_ANCHOR, NavigatorMessages.getString("NAV_RequiredMethod"))).append("</h2>"); buffer.append("<table border='0'><tr><td>"); if (isRequiredClassMethod) { buffer.append(NavigatorMessages.getString("NAV_RequiredMethodYes")); } else { buffer.append(NavigatorMessages.getString("NAV_RequiredMethodNo")); } buffer.append("</td></tr></table>"); } else { returnValueDesc = null; buffer.append(getFunctionalAgentOrInstanceMethodArgumentsAndReturnValueHtml(owner, metadata.getArguments(), returnValueDesc, url, caldoc)); } buffer.append(getFunctionalAgentOrInstanceMethodExamplesHtml(owner, metadata.getExamples())); return buffer.toString(); } /** * @param argMeta the metadata for the arguments * @param returnValueDesc the return value description in the metadata * @param url the address of the page * @param caldoc the CALDoc comment associated with the entity, or null if there is none * @return the HTML for the argument and return value information of the entity */ private static String getFunctionalAgentOrInstanceMethodArgumentsAndReturnValueHtml(NavFrameOwner owner, ArgumentMetadata[] argMeta, String returnValueDesc, NavAddress url, CALDocComment caldoc) { StringBuilder buffer = new StringBuilder(); ScopedEntityNamingPolicy namingPolicy = new UnqualifiedUnlessAmbiguous(owner.getPerspective().getWorkingModuleTypeInfo()); String[] normalStrings = NavAddressHelper.getTypeStrings(owner, url, namingPolicy); String[] qualifiedStrings = NavAddressHelper.getTypeStrings(owner, url, ScopedEntityNamingPolicy.FULLY_QUALIFIED); if (normalStrings.length > 0) { // A collector that is not connected does not have a result type. // Display the result type. buffer.append("<h2>" + getAnchorHtml(RESULT_ANCHOR, NavigatorMessages.getString("NAV_ReturnValue_Header")) + "</h2>"); buffer.append("<table border='0'>"); buffer.append("<tr><td nowrap valign='top'>"); String typeString = getTypeStringHtml(owner, qualifiedStrings[qualifiedStrings.length - 1], normalStrings[normalStrings.length - 1]); buffer.append("<tt><b><i>" + NavigatorMessages.getString("NAV_ReturnValueIndicator") + "</i></b> :: <i>" + typeString + "</i></tt></td>"); if (returnValueDesc != null) { buffer.append("<td nowrap valign='top'> - "); buffer.append(returnValueDesc); buffer.append("</td></tr></table>"); } else { if (caldoc != null && caldoc.getReturnBlock() != null) { buffer.append("<td nowrap valign='top'> - </td><td valign='top'>"); buffer.append(NavHtmlFactory.getHtmlForCALDocTextBlock(caldoc.getReturnBlock(), owner, false)); buffer.append("</td></tr></table>"); } buffer.append("</tr></table>"); } } // Display the argument information. buffer.append("<h2>" + getAnchorHtml(ARGUMENTS_ANCHOR, NavigatorMessages.getString("NAV_Arguments_Header")) + "</h2>"); NavAddressHelper.adjustArgumentNames(owner, url, argMeta); if (argMeta.length == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { for (int i = 0; i < argMeta.length; i++) { buffer.append("<table border='0'>"); buffer.append("<tr><td nowrap valign='top'>"); String typeString = getTypeStringHtml(owner, qualifiedStrings[i], normalStrings[i]); buffer.append("<tt><b>" + argMeta[i].getDisplayName() + "</b> :: <i>" + typeString + "</i></tt></td>"); String argDescription = argMeta[i].getShortDescription(); if (argDescription != null) { buffer.append("<td nowrap valign='top'> - "); buffer.append(argDescription); buffer.append("</td></tr></table>"); } else { if (caldoc != null && i < caldoc.getNArgBlocks()) { buffer.append("<td nowrap valign='top'> - </td><td valign='top'>"); buffer.append(NavHtmlFactory.getHtmlForCALDocTextBlock(caldoc.getNthArgBlock(i).getTextBlock(), owner, false)); buffer.append("</td></tr></table>"); } buffer.append("</tr></table>"); } if (argMeta[i].getDefaultValuesExpression() != null) { buffer.append("<font size='-1'>"); buffer.append(NavigatorMessages.getString("NAV_DefaultValues_Message")); if (argMeta[i].useDefaultValuesOnly()) { buffer.append(NavigatorMessages.getString("NAV_DefaultValuesOnly_Message")); } buffer.append("</font>"); } } } return buffer.toString(); } /** * @param owner the navigator owner * @param examples the included examples in the metadata * @return the HTML for the entity's example metadata */ private static String getFunctionalAgentOrInstanceMethodExamplesHtml(NavFrameOwner owner, CALExample[] examples) { StringBuilder buffer = new StringBuilder(); // Display the examples buffer.append("<h2>" + getAnchorHtml(EXAMPLES_ANCHOR, NavigatorMessages.getString("NAV_Examples_Header")) + "</h2>"); if (examples.length == 0) { buffer.append(NavigatorMessages.getString("NAV_NoValue")); } else { for (int i = 0; i < examples.length; i++) { CALExpression expression = examples[i].getExpression(); String description = examples[i].getDescription() != null ? examples[i].getDescription() : NavigatorMessages.getString("NAV_NoValue"); buffer.append("<h3>" + NavigatorMessages.getString("NAV_ExampleNumber", Integer.valueOf(i+1)) + "</h3>"); buffer.append("<table border='0' width='100%'>"); buffer.append("<tr>"); buffer.append("<td rowspan='3' width='20'> </td>"); buffer.append("<td width='120'>" + NavigatorMessages.getString("NAV_Description") + "</td>"); buffer.append("<td>" + description + "</td>"); buffer.append("</tr>"); buffer.append("<tr>"); buffer.append("<td>" + NavigatorMessages.getString("NAV_Expression") + "</td>"); if (expression.getQualifiedExpressionText().equals("")) { buffer.append("<td><tt>" + expression.getExpressionText() + "</tt></td>"); } else { buffer.append("<td><tt>" + expression.getQualifiedExpressionText() + "</tt></td>"); } buffer.append("</tr>"); if (examples[i].evaluateExample()) { buffer.append("<tr>"); buffer.append("<td>" + NavigatorMessages.getString("NAV_Result") + "</td>"); buffer.append("<td><tt>" + getExpressionHtml(owner, expression)+ "</tt></td>"); buffer.append("</tr>"); } buffer.append("</table>"); } // remove trailing   buffer.delete(buffer.length() - 6, buffer.length()); } return buffer.toString(); } /** * Generate the HTML for the summary of a CALDoc comment. * @param caldoc the CALDoc comment. * @param locale the locale for the documentation generation. * @param alwaysAsNewParagraph whether the generated HTML should always start a new paragraph (e.g. with a <p> block). * @return the HTML for the summary of a CALDoc comment. */ public static String getHtmlForCALDocSummaryWithNoHyperlinks(CALDocComment caldoc, Locale locale, boolean alwaysAsNewParagraph) { String contents = NavigatorMessages.getString( "NAV_CALDoc", CALDocToHTMLUtilities.getHTMLForCALDocSummary( caldoc, new NoHyperlinkCrossReferenceGenerator(), locale, NavigatorMessages.getString("NAV_CALDocReturnsColon"))); if (alwaysAsNewParagraph) { return "<p>" + contents; } else { return contents; } } /** * Generate the HTML for a text block appearing in a CALDoc comment. * @param textBlock the text block to be formatted as HTML. * @param alwaysAsNewParagraph whether the generated HTML should always start a new paragraph (e.g. with a <p> block). * @return the HTML for the text block. */ public static String getHtmlForCALDocTextBlockWithNoHyperlinks(CALDocComment.TextBlock textBlock, boolean alwaysAsNewParagraph) { String contents = NavigatorMessages.getString("NAV_CALDoc", CALDocToHTMLUtilities.getHTMLForCALDocTextBlock(textBlock, new NoHyperlinkCrossReferenceGenerator())); if (alwaysAsNewParagraph) { return "<p>" + contents; } else { return contents; } } /** * Generate the HTML for the summary of a CALDoc comment. * @param caldoc the CALDoc comment. * @param owner the navigator owner. * @param alwaysAsNewParagraph whether the generated HTML should always start a new paragraph (e.g. with a <p> block). * @return the HTML for the summary of a CALDoc comment. */ public static String getHtmlForCALDocSummary(CALDocComment caldoc, NavFrameOwner owner, boolean alwaysAsNewParagraph) { String contents = NavigatorMessages.getString( "NAV_CALDoc", CALDocToHTMLUtilities.getHTMLForCALDocSummary( caldoc, new CrossReferenceGenerator(owner), owner.getLocaleForMetadata(), NavigatorMessages.getString("NAV_CALDocReturnsColon"))); if (alwaysAsNewParagraph) { return "<p>" + contents; } else { return contents; } } /** * Generate the HTML for a text block appearing in a CALDoc comment. * @param textBlock the text block to be formatted as HTML. * @param owner the navigator owner. * @param alwaysAsNewParagraph whether the generated HTML should always start a new paragraph (e.g. with a <p> block). * @return the HTML for the text block. */ public static String getHtmlForCALDocTextBlock(CALDocComment.TextBlock textBlock, NavFrameOwner owner, boolean alwaysAsNewParagraph) { String contents = NavigatorMessages.getString("NAV_CALDoc", CALDocToHTMLUtilities.getHTMLForCALDocTextBlock(textBlock, new CrossReferenceGenerator(owner))); if (alwaysAsNewParagraph) { return "<p>" + contents; } else { return contents; } } /** * @param owner the navigator owner * @param url the address of the page * @return the HTML for displaying the location of the page in the page hierarchy */ private static String getPageLocationHtml(NavFrameOwner owner, NavAddress url) { String separator = " >> "; StringBuilder buffer = new StringBuilder(); CALFeatureMetadata metadata = NavAddressHelper.getMetadata(owner, url); buffer.append("<table cellpadding='1' cellspacing='0' width='100%' bgcolor='#E0E0E0'><tr><td nowrap>"); buffer.append("<font size='3'><tt>"); buffer.append(getLinkHtml(NavAddress.getRootAddress(NavAddress.WORKSPACE_METHOD), NavigatorMessages.getString("NAV_Workspace_Location"))); buffer.append(separator); if (url.getMethod() == NavAddress.SEARCH_METHOD) { buffer.append(NavigatorMessages.getString("NAV_SearchResults_Location")); } else if (url.getMethod() == NavAddress.COLLECTOR_METHOD) { buffer.append(NavAddressHelper.getDisplayText(owner, url) + NavigatorMessages.getString("NAV_Collector_Location")); } else if (url.getParameter(NavAddress.VAULT_PARAMETER) != null) { // add link to module buffer.append(getLinkHtml(url.withAllStripped(), NavAddressHelper.getDisplayText(owner, url.withAllStripped()))); buffer.append(separator); // add a link to the vault String vault = url.getParameter(NavAddress.VAULT_PARAMETER); if (vault.equals(NavAddress.TYPE_VAULT_VALUE)) { buffer.append(NavigatorMessages.getString("NAV_TypeVault_Location")); } else if (vault.equals(NavAddress.CLASS_VAULT_VALUE)) { buffer.append(NavigatorMessages.getString("NAV_ClassVault_Location")); } else if (vault.equals(NavAddress.FUNCTION_VAULT_VALUE)) { buffer.append(NavigatorMessages.getString("NAV_FunctionVault_Location")); } else if (vault.equals(NavAddress.INSTANCE_VAULT_VALUE)) { buffer.append(NavigatorMessages.getString("NAV_InstanceVault_Location")); } } else if (metadata instanceof ScopedEntityMetadata || metadata instanceof ClassInstanceMetadata || metadata instanceof InstanceMethodMetadata) { String typeString = null; CALFeatureName featureName = metadata.getFeatureName(); // add link to module ModuleName moduleName = featureName.toModuleName(); MetaModule module = owner.getPerspective().getMetaModule(moduleName); NavAddress moduleUrl = NavAddress.getAddress(module); buffer.append(getLinkHtml(NavAddress.getAddress(module), moduleName.toSourceText())); buffer.append(separator); // add link to parent or vault if (metadata instanceof FunctionMetadata) { NavAddress vaultUrl = moduleUrl.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.FUNCTION_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_FunctionVault_Location"))); typeString = NavigatorMessages.getString("NAV_Function_Location"); } else if (metadata instanceof TypeConstructorMetadata) { NavAddress vaultUrl = moduleUrl.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.TYPE_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_TypeVault_Location"))); typeString = NavigatorMessages.getString("NAV_Type_Location"); } else if (metadata instanceof TypeClassMetadata) { NavAddress vaultUrl = moduleUrl.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.CLASS_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_ClassVault_Location"))); typeString = NavigatorMessages.getString("NAV_Class_Location"); } else if (metadata instanceof ClassInstanceMetadata) { NavAddress vaultUrl = moduleUrl.withParameter(NavAddress.VAULT_PARAMETER, NavAddress.INSTANCE_VAULT_VALUE); buffer.append(getLinkHtml(vaultUrl, NavigatorMessages.getString("NAV_InstanceVault_Location"))); typeString = NavigatorMessages.getString("NAV_Instance_Location"); } else if (metadata instanceof InstanceMethodMetadata) { CALFeatureName methodFeatureName = metadata.getFeatureName(); NavAddress parentUrl = NavAddress.getAddress(CALFeatureName.getClassInstanceFeatureName( methodFeatureName.toInstanceIdentifier(), methodFeatureName.toModuleName())); buffer.append(getLinkHtml(parentUrl, NavAddressHelper.getDisplayText(owner, parentUrl, ScopedEntityNamingPolicy.UNQUALIFIED))); typeString = NavigatorMessages.getString("NAV_InstanceMethod_Location"); } else if (metadata instanceof ClassMethodMetadata) { typeString = NavigatorMessages.getString("NAV_ClassMethod_Location"); // figure out the type class this method belongs to TypeClass parentClass = null; CALFeatureName methodFeatureName = ((ClassMethodMetadata) metadata).getFeatureName(); QualifiedName methodName = methodFeatureName.toQualifiedName(); int classCount = module.getTypeInfo().getNTypeClasses(); for (int i = 0; i < classCount; i++) { TypeClass typeClass = module.getTypeInfo().getNthTypeClass(i); int methodCount = typeClass.getNClassMethods(); for (int n = 0; n < methodCount; n++) { if (typeClass.getNthClassMethod(n).getName().equals(methodName)) { parentClass = typeClass; break; } } if (parentClass != null) { break; } } if (parentClass != null) { NavAddress parentUrl = NavAddress.getAddress(parentClass); buffer.append(getLinkHtml(parentUrl, NavAddressHelper.getDisplayText(owner, parentUrl, ScopedEntityNamingPolicy.UNQUALIFIED))); } else { buffer.append("<font color='red'>" + NavigatorMessages.getString("NAV_ClassNotFound_Location") + "</font>"); } } else if (metadata instanceof DataConstructorMetadata) { typeString = NavigatorMessages.getString("NAV_Constructor_Location"); // figure out the type constructor this data constructor is for CALFeatureName dataConsFeatureName = ((DataConstructorMetadata) metadata).getFeatureName(); QualifiedName dataConsName = dataConsFeatureName.toQualifiedName(); TypeConstructor parentCons = null; int typeConsCount = module.getTypeInfo().getNTypeConstructors(); for (int i = 0; i < typeConsCount; i++) { TypeConstructor typeCons = module.getTypeInfo().getNthTypeConstructor(i); int dataConsCount = typeCons.getNDataConstructors(); for (int n = 0; n < dataConsCount; n++) { if (typeCons.getNthDataConstructor(n).getName().equals(dataConsName)) { parentCons = typeCons; break; } } if (parentCons != null) { break; } } if (parentCons != null) { NavAddress parentUrl = NavAddress.getAddress(parentCons); buffer.append(getLinkHtml(parentUrl, NavAddressHelper.getDisplayText(owner, parentUrl, ScopedEntityNamingPolicy.UNQUALIFIED))); } else { buffer.append("<font color='red'>" + NavigatorMessages.getString("NAV_ConstructorNotFound_Location") + "</font>"); } } buffer.append(separator); // add a link to entity itself buffer.append(NavAddressHelper.getDisplayText(owner, url, ScopedEntityNamingPolicy.UNQUALIFIED) + typeString); } else if (metadata instanceof ModuleMetadata) { // add a link to the module buffer.append(NavAddressHelper.getDisplayText(owner, url) + NavigatorMessages.getString("NAV_Module_Location")); } buffer.append("</tt></font>"); buffer.append("</td></tr></table>"); return buffer.toString(); } /** * @return the HTML code that is included on top of every page to define style elements */ private static String getHeaderHtml() { StringBuilder header = new StringBuilder(); // Here we define several styles for the HTML rendered. Unfortunately not everything can // be defined using styles and we still have to use the <code> tag in the HTML. // This is because the Swing HTML widget does not render all CSS styles correctly. header.append("<html><head><style type='text/css'>\n"); header.append("body { font-family: sans-serif }\n"); header.append("a { text-decoration: none }\n"); header.append("code { font-size: larger }\n"); header.append("</style></head>\n"); header.append("<body bgcolor='#ffffff'>\n"); return header.toString(); } /** * @return the HTML code that forms the footer of every page */ private static String getFooterHtml() { return "</body></html>"; } /** * A utility function for evaluating a CALExpression and returning the result as * an HTML formatted String. * @param expression the expression to evaluate * @return the HTML formatted result String */ private static String getExpressionHtml(NavFrameOwner owner, CALExpression expression) { StringBuilder result = new StringBuilder(); boolean success = EditorHelper.evaluateExpression(owner, expression, result); // Encode result to html result = new StringBuilder(HtmlHelper.htmlEncode(result.toString())); if (!success) { return "<font color='red'>" + result.toString() + "</font>"; } else { return result.toString(); } } private static String getTypeStringHtml(NavFrameOwner owner, TypeExpr typeExpr, ScopedEntityNamingPolicy namingPolicy) { String qualified = typeExpr.toString(ScopedEntityNamingPolicy.FULLY_QUALIFIED); String custom = typeExpr.toString(namingPolicy); return getTypeStringHtml(owner, qualified, custom); } private static String getTypeStringHtml(NavFrameOwner owner, String qualified, String custom) { // We include '-' and '>' in the delimiters so that '->' in type expressions // doesn't get hyperlinked to 'Prelude.->'. Although technically that is a valid // link, it doesn't look very nice if all arrows in the expressions are links. String delims = "[](),-> "; StringBuilder buffer = new StringBuilder(); StringTokenizer qualifiedTokens = new StringTokenizer(qualified, delims, true); StringTokenizer customTokens = new StringTokenizer(custom, delims, true); // Both tokenizers should returns the same number of tokens. while (qualifiedTokens.hasMoreTokens()) { String qualifiedToken = qualifiedTokens.nextToken(); String customToken = customTokens.nextToken(); if (delims.indexOf(qualifiedToken) == -1 && QualifiedName.isValidCompoundName(qualifiedToken)) { // In a TypeExpr we are either dealing with a type class or type constructor. QualifiedName name = QualifiedName.makeFromCompoundName(qualifiedToken); MetaModule metaModule = owner.getPerspective().getMetaModule(name.getModuleName()); if (metaModule != null) { TypeClass typeClass = metaModule.getTypeInfo().getTypeClass(name.getUnqualifiedName()); TypeConstructor typeCons = metaModule.getTypeInfo().getTypeConstructor(name.getUnqualifiedName()); if (typeCons != null) { customToken = getLinkHtml(NavAddress.getAddress(typeCons), customToken); } else if (typeClass != null) { customToken = getLinkHtml(NavAddress.getAddress(typeClass), customToken); } } } buffer.append(customToken); } return buffer.toString(); } /** * @param link the location the link should point to * @param name the display text of the link * @return the HTML link code */ private static String getLinkHtml(String link, String name) { return (link == null) ? name : "<a href='" + link + "'>" + name + "</a>"; } /** * @param url the url to link to * @param name the display name of the link * @return the HTML link code */ private static String getLinkHtml(NavAddress url, String name) { return getLinkHtml(url.toString(), name); } /** * A utility function to return the HTML code to create an anchor. * @param anchor the anchor name (must start with '#') * @param name the display text of the anchor * @return the HTML anchor code */ private static String getAnchorHtml(String anchor, String name) { return (anchor == null || !anchor.startsWith("#")) ? name : "<a name='" + anchor.substring(1) + "'>" + name + "</a>"; } /** * A utility function to return the HTML code to created a disabled * piece of text (eg, a "related feature" that cannot be linked to * because it doesn't actually exist) * @param text Text to be shown in disabled style * @return the HTML code showing text in disabled style */ private static String getDisabledHtml(String text) { return (text == null) ? "" : "<font color='gray'>" + text + "</font>"; } } /** * A comparator for sorting a list of ScopedEntity by the unqualified name. * @author Frank Worsley */ class ScopedEntityComparator implements Comparator<ScopedEntity> { public int compare(ScopedEntity e1, ScopedEntity e2) { return e1.getName().getUnqualifiedName().compareTo(e2.getName().getUnqualifiedName()); } } /** * A comparator for sorting a list of ScopedEntity by the qualified name. * @author Frank Worsley */ class ScopedEntityQualifiedComparator implements Comparator<ScopedEntity> { public int compare(ScopedEntity e1, ScopedEntity e2) { return e1.getName().compareTo(e2.getName()); } } /** * A comparator for sorting a list of MetaModule by the module name. * @author Frank Worsley */ class MetaModuleComparator implements Comparator<MetaModule> { public int compare(MetaModule m1, MetaModule m2) { return m1.getName().compareTo(m2.getName()); } } /** * A comparator for sorting a list of ModuleTypeInfo by the module name. * @author Frank Worsley */ class ModuleTypeInfoComparator implements Comparator<ModuleTypeInfo> { public int compare(ModuleTypeInfo m1, ModuleTypeInfo m2) { return m1.getModuleName().compareTo(m2.getModuleName()); } } /** * A comparator for sorting a list of ClassInstance by the instance name with context. * @author Frank Worsley */ class ClassInstanceComparator implements Comparator<ClassInstance> { public int compare(ClassInstance i1, ClassInstance i2) { //compare instance names without their context. Ignore the first (, so that parenthesizing the type constructor doesn't affect order. //e.g. Outputable Double is before Outputable (Either a b). String name1 = i1.getName().replaceFirst("\\(", ""); String name2 = i2.getName().replaceFirst("\\(", ""); return name1.compareTo(name2); } } /** * A comparator for sorting NavAddress instances using the best name of their metadata or * their CAL name if there is no metadata. * @author Frank Worsley */ class NavAddressComparator implements Comparator<NavAddress> { private final NavFrameOwner owner; public NavAddressComparator(NavFrameOwner owner) { this.owner = owner; } /** * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(NavAddress u1, NavAddress u2) { return NavAddressHelper.getDisplayText(owner, u1).compareTo(NavAddressHelper.getDisplayText(owner, u2)); } }