Put back hyperlink classes
diff --git a/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/BaseHyperlinkDetector.java b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/BaseHyperlinkDetector.java
new file mode 100644
index 0000000..ca26c9a
--- /dev/null
+++ b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/BaseHyperlinkDetector.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - Initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *******************************************************************************/
+
+package org.eclipse.wst.xsd.editor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Base class for hyperlinks detectors. Provides a framework and common code for
+ * hyperlink detectors. TODO: Can we pull this class further up the inheritance
+ * hierarchy?
+ */
+public abstract class BaseHyperlinkDetector implements IHyperlinkDetector
+{
+  /*
+   * (non-Javadoc)
+   */
+  public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks)
+  {
+    if (region == null || textViewer == null)
+    {
+      return null;
+    }
+
+    List hyperlinks = new ArrayList(0);
+    IDocument document = textViewer.getDocument();
+    int offset = region.getOffset();
+
+    IDOMNode node = getCurrentNode(document, offset);
+
+    // This call allows us to determine whether an attribute is linkable,
+    // without incurring the cost of asking for the target component.
+
+    if (!isLinkable(node))
+    {
+      return null;
+    }
+
+    IRegion hyperlinkRegion = getHyperlinkRegion(node);
+
+    // createHyperlink is a template method. Derived classes, should override.
+
+    IHyperlink hyperlink = createHyperlink(document, node, hyperlinkRegion);
+
+    if (hyperlink != null)
+    {
+      hyperlinks.add(hyperlink);
+    }
+
+    if (hyperlinks.size() == 0)
+    {
+      return null;
+    }
+
+    return (IHyperlink[]) hyperlinks.toArray(new IHyperlink[0]);
+  }
+
+  /**
+   * Determines whether a node is "linkable" that is, the component it refers to
+   * can be the target of a "go to definition" navigation.
+   * 
+   * @param node the node to test, must not be null;
+   * @return true if the node is linkable, false otherwise.
+   */
+  private boolean isLinkable(IDOMNode node)
+  {
+    if (node == null)
+    {
+      return false;
+    }
+
+    short nodeType = node.getNodeType();
+
+    boolean isLinkable = false;
+
+    if (nodeType == Node.ATTRIBUTE_NODE)
+    {
+      IDOMAttr attr = (IDOMAttr) node;
+      String name = attr.getName();
+
+      // isLinkableAttribute is a template method. Derived classes should
+      // override.
+
+      isLinkable = isLinkableAttribute(name);
+    }
+
+    return isLinkable;
+  }
+
+  /**
+   * Determines whether an attribute is "linkable" that is, the component it
+   * points to can be the target of a "go to definition" navigation. Derived
+   * classes should override.
+   * 
+   * @param name the attribute name. Must not be null.
+   * @return true if the attribute is linkable, false otherwise.
+   */
+  protected abstract boolean isLinkableAttribute(String name);
+
+  /**
+   * Creates a hyperlink based on the selected node. Derived classes should
+   * override.
+   * 
+   * @param document the source document.
+   * @param node the node under the cursor.
+   * @param region the text region to use to create the hyperlink.
+   * @return a new IHyperlink for the node or null if one cannot be created.
+   */
+  protected abstract IHyperlink createHyperlink(IDocument document, IDOMNode node, IRegion region);
+
+  /**
+   * Locates the attribute node under the cursor.
+   * 
+   * @param offset the cursor offset.
+   * @param parent the parent node
+   * @return an IDOMNode representing the attribute if one is found at the
+   *         offset or null otherwise.
+   */
+  protected IDOMNode getAttributeNode(int offset, IDOMNode parent)
+  {
+    IDOMAttr attrNode = null;
+    NamedNodeMap map = parent.getAttributes();
+
+    for (int index = 0; index < map.getLength(); index++)
+    {
+      attrNode = (IDOMAttr) map.item(index);
+      boolean located = attrNode.contains(offset);
+      if (located)
+      {
+        if (attrNode.hasNameOnly())
+        {
+          attrNode = null;
+        }
+        break;
+      }
+    }
+
+    if (attrNode == null)
+    {
+      return parent;
+    }
+    return attrNode;
+  }
+
+  /**
+   * Returns the node the cursor is currently on in the document or null if no
+   * node is selected
+   * 
+   * @param offset the current cursor offset.
+   * @return IDOMNode either element, doctype, text, attribute or null
+   */
+  private IDOMNode getCurrentNode(IDocument document, int offset)
+  {
+    IndexedRegion inode = null;
+    IStructuredModel sModel = null;
+
+    try
+    {
+      sModel = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+      inode = sModel.getIndexedRegion(offset);
+      if (inode == null)
+        inode = sModel.getIndexedRegion(offset - 1);
+    }
+    finally
+    {
+      if (sModel != null)
+        sModel.releaseFromRead();
+    }
+
+    if (inode instanceof IDOMNode)
+    {
+      IDOMNode node = (IDOMNode) inode;
+
+      if (node.hasAttributes())
+      {
+        node = getAttributeNode(offset, node);
+      }
+      return node;
+    }
+
+    return null;
+  }
+
+  /**
+   * Get the text region corresponding to an IDOMNode.
+   * 
+   * @param node the node for which we want the text region. Must not be null.
+   * @return an IRegion for the node, or null if the node is not recognized.
+   */
+  protected IRegion getHyperlinkRegion(IDOMNode node)
+  {
+    if (node == null)
+    {
+      return null;
+    }
+
+    IRegion hyperRegion = null;
+    short nodeType = node.getNodeType();
+
+    switch (nodeType)
+    {
+      case Node.ELEMENT_NODE : 
+        {
+          hyperRegion = new Region(node.getStartOffset(), node.getEndOffset() - node.getStartOffset());
+        }
+      break;
+      case Node.ATTRIBUTE_NODE : 
+        {
+          IDOMAttr att = (IDOMAttr) node;
+  
+          int regOffset = att.getValueRegionStartOffset();
+  
+          // ISSUE: We are using a deprecated method here. Is there
+          // a better way to get what we need?
+  
+          ITextRegion valueRegion = att.getValueRegion();
+          if (valueRegion != null)
+          {
+            int regLength = valueRegion.getTextLength();
+            String attValue = att.getValueRegionText();
+  
+            // Do not include quotes in attribute value region.
+            if (StringUtils.isQuoted(attValue))
+            {
+              regOffset = ++regOffset;
+              regLength = regLength - 2;
+            }
+            hyperRegion = new Region(regOffset, regLength);
+          }
+        }
+        break;
+      default :
+        // Do nothing.
+        break;
+    }
+ 
+    return hyperRegion;
+  }
+}
diff --git a/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/Logger.java b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/Logger.java
new file mode 100644
index 0000000..a53578c
--- /dev/null
+++ b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/Logger.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2001, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *     
+ *******************************************************************************/
+package org.eclipse.wst.xsd.editor;
+
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.osgi.framework.Bundle;
+
+/**
+ * Small convenience class to log messages to plugin's log file and also, if
+ * desired, the console. This class should only be used by classes in this
+ * plugin. Other plugins should make their own copy, with appropriate ID.
+ */
+public class Logger {
+	private static final String PLUGIN_ID = "org.eclipse.wst.xsd.ui"; //$NON-NLS-1$
+	
+	public static final int ERROR = IStatus.ERROR; // 4
+	public static final int ERROR_DEBUG = 200 + ERROR;
+	public static final int INFO = IStatus.INFO; // 1
+	public static final int INFO_DEBUG = 200 + INFO;
+
+	public static final int OK = IStatus.OK; // 0
+
+	public static final int OK_DEBUG = 200 + OK;
+
+	private static final String TRACEFILTER_LOCATION = "/debug/tracefilter"; //$NON-NLS-1$
+	public static final int WARNING = IStatus.WARNING; // 2
+	public static final int WARNING_DEBUG = 200 + WARNING;
+
+	/**
+	 * Adds message to log.
+	 * 
+	 * @param level
+	 *            severity level of the message (OK, INFO, WARNING, ERROR,
+	 *            OK_DEBUG, INFO_DEBUG, WARNING_DEBUG, ERROR_DEBUG)
+	 * @param message
+	 *            text to add to the log
+	 * @param exception
+	 *            exception thrown
+	 */
+	protected static void _log(int level, String message, Throwable exception) {
+		if (level == OK_DEBUG || level == INFO_DEBUG || level == WARNING_DEBUG || level == ERROR_DEBUG) {
+			if (!isDebugging())
+				return;
+		}
+
+		int severity = IStatus.OK;
+		switch (level) {
+			case INFO_DEBUG :
+			case INFO :
+				severity = IStatus.INFO;
+				break;
+			case WARNING_DEBUG :
+			case WARNING :
+				severity = IStatus.WARNING;
+				break;
+			case ERROR_DEBUG :
+			case ERROR :
+				severity = IStatus.ERROR;
+		}
+		message = (message != null) ? message : "null"; //$NON-NLS-1$
+		Status statusObj = new Status(severity, PLUGIN_ID, severity, message, exception);
+		Bundle bundle = Platform.getBundle(PLUGIN_ID);
+		if (bundle != null) 
+			Platform.getLog(bundle).log(statusObj);
+	}
+
+	/**
+	 * Prints message to log if category matches /debug/tracefilter option.
+	 * 
+	 * @param message
+	 *            text to print
+	 * @param category
+	 *            category of the message, to be compared with
+	 *            /debug/tracefilter
+	 */
+	protected static void _trace(String category, String message, Throwable exception) {
+		if (isTracing(category)) {
+			message = (message != null) ? message : "null"; //$NON-NLS-1$
+			Status statusObj = new Status(IStatus.OK, PLUGIN_ID, IStatus.OK, message, exception);
+			Bundle bundle = Platform.getBundle(PLUGIN_ID);
+			if (bundle != null) 
+				Platform.getLog(bundle).log(statusObj);
+		}
+	}
+
+	/**
+	 * @return true if the platform is debugging
+	 */
+	public static boolean isDebugging() {
+		return Platform.inDebugMode();
+	}
+
+	/**
+	 * Determines if currently tracing a category
+	 * 
+	 * @param category
+	 * @return true if tracing category, false otherwise
+	 */
+	public static boolean isTracing(String category) {
+		if (!isDebugging())
+			return false;
+
+		String traceFilter = Platform.getDebugOption(PLUGIN_ID + TRACEFILTER_LOCATION);
+		if (traceFilter != null) {
+			StringTokenizer tokenizer = new StringTokenizer(traceFilter, ","); //$NON-NLS-1$
+			while (tokenizer.hasMoreTokens()) {
+				String cat = tokenizer.nextToken().trim();
+				if (category.equals(cat)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	public static void log(int level, String message) {
+		_log(level, message, null);
+	}
+
+	public static void log(int level, String message, Throwable exception) {
+		_log(level, message, exception);
+	}
+
+	public static void logException(String message, Throwable exception) {
+		_log(ERROR, message, exception);
+	}
+
+	public static void logException(Throwable exception) {
+		_log(ERROR, exception.getMessage(), exception);
+	}
+
+	public static void trace(String category, String message) {
+		_trace(category, message, null);
+	}
+
+	public static void traceException(String category, String message, Throwable exception) {
+		_trace(category, message, exception);
+	}
+
+	public static void traceException(String category, Throwable exception) {
+		_trace(category, exception.getMessage(), exception);
+	}
+}
diff --git a/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/XSDHyperlink.java b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/XSDHyperlink.java
new file mode 100644
index 0000000..bd8ee37
--- /dev/null
+++ b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/XSDHyperlink.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - Initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *******************************************************************************/
+
+package org.eclipse.wst.xsd.editor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
+import org.eclipse.xsd.XSDConcreteComponent;
+import org.eclipse.xsd.XSDSchema;
+
+/**
+ * XSDHyperlink knows how to open links from XSD files.
+ * 
+ * @see XSDHyperlinkDetector
+ */
+public class XSDHyperlink implements IHyperlink
+{
+  private IRegion fRegion;
+  private XSDConcreteComponent fComponent;
+
+  public XSDHyperlink(IRegion region, XSDConcreteComponent component)
+  {
+    fRegion = region;
+    fComponent = component;
+  }
+
+  public IRegion getHyperlinkRegion()
+  {
+    return fRegion;
+  }
+
+  public String getTypeLabel()
+  {
+    return null;
+  }
+
+  public String getHyperlinkText()
+  {
+    return null;
+  }
+
+  public void open()
+  {
+    XSDSchema schema = fComponent.getSchema();
+
+    if (schema == null)
+    {
+      return;
+    }
+
+    String schemaLocation = schema.getSchemaLocation();
+    schemaLocation = URIHelper.removePlatformResourceProtocol(schemaLocation);
+    IPath schemaPath = new Path(schemaLocation);
+    IFile schemaFile = ResourcesPlugin.getWorkspace().getRoot().getFile(schemaPath);
+
+    boolean fileExists = schemaFile != null && schemaFile.exists();
+
+    if (!fileExists)
+    {
+      return;
+    }
+    IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+    if (workbenchWindow != null)
+    {
+      IWorkbenchPage workbenchPage = workbenchWindow.getActivePage();
+      IEditorPart editorPart = workbenchPage.getActiveEditor();
+      
+      workbenchPage.getNavigationHistory().markLocation(editorPart);
+      
+      try
+      {
+        editorPart = IDE.openEditor(workbenchPage, schemaFile, true);
+        if (editorPart instanceof InternalXSDMultiPageEditor)
+        {
+          ((InternalXSDMultiPageEditor) editorPart).openOnGlobalReference(fComponent);
+        }
+      }
+      catch (PartInitException pie)
+      {
+        Logger.log(Logger.WARNING_DEBUG, pie.getMessage(), pie);
+      }
+    }
+  }
+}
diff --git a/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/XSDHyperlinkDetector.java b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/XSDHyperlinkDetector.java
new file mode 100644
index 0000000..7bdfa15
--- /dev/null
+++ b/bundles/org.eclipse.wst.xsd.ui/src-adt-xsd/org/eclipse/wst/xsd/editor/XSDHyperlinkDetector.java
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - Initial API and implementation
+ *     Jens Lukowski/Innoopract - initial renaming/restructuring
+ *******************************************************************************/
+
+package org.eclipse.wst.xsd.editor;
+
+import java.util.List;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xsd.ui.internal.text.XSDModelAdapter;
+import org.eclipse.xsd.XSDAttributeDeclaration;
+import org.eclipse.xsd.XSDAttributeGroupDefinition;
+import org.eclipse.xsd.XSDConcreteComponent;
+import org.eclipse.xsd.XSDElementDeclaration;
+import org.eclipse.xsd.XSDIdentityConstraintDefinition;
+import org.eclipse.xsd.XSDModelGroupDefinition;
+import org.eclipse.xsd.XSDSchema;
+import org.eclipse.xsd.XSDSchemaDirective;
+import org.eclipse.xsd.XSDSimpleTypeDefinition;
+import org.eclipse.xsd.XSDTypeDefinition;
+import org.eclipse.xsd.XSDVariety;
+import org.eclipse.xsd.util.XSDConstants;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+
+/**
+ * Detects hyperlinks for XSD files. Used by the XSD text editor to provide a
+ * "Go to declaration" functionality similar with the one provided by the Java
+ * editor.
+ */
+public class XSDHyperlinkDetector extends BaseHyperlinkDetector
+{
+  /**
+   * Determines whether an attribute is "linkable" that is, the component it
+   * points to can be the target of a "go to definition" navigation. Derived
+   * classes should override.
+   * 
+   * @param name the attribute name. Must not be null.
+   * @return true if the attribute is linkable, false otherwise.
+   */
+  protected boolean isLinkableAttribute(String name)
+  {
+    boolean isLinkable = name.equals(XSDConstants.TYPE_ATTRIBUTE) ||
+      name.equals(XSDConstants.REFER_ATTRIBUTE) || 
+      name.equals(XSDConstants.REF_ATTRIBUTE) || 
+      name.equals(XSDConstants.BASE_ATTRIBUTE) || 
+      name.equals(XSDConstants.SCHEMALOCATION_ATTRIBUTE) || 
+      name.equals(XSDConstants.SUBSTITUTIONGROUP_ATTRIBUTE) ||
+      name.equals(XSDConstants.ITEMTYPE_ATTRIBUTE) ||
+      name.equals(XSDConstants.MEMBERTYPES_ATTRIBUTE)
+      ;
+
+    return isLinkable;
+  }
+
+  /**
+   * Creates a hyperlink based on the selected node. Derived classes should
+   * override.
+   * 
+   * @param document the source document.
+   * @param node the node under the cursor.
+   * @param region the text region to use to create the hyperlink.
+   * @return a new IHyperlink for the node or null if one cannot be created.
+   */
+  protected IHyperlink createHyperlink(IDocument document, IDOMNode node, IRegion region)
+  {
+    XSDSchema xsdSchema = getXSDSchema(document);
+
+    if (xsdSchema == null)
+    {
+      return null;
+    }
+
+    XSDConcreteComponent targetComponent = getTargetXSDComponent(xsdSchema, node);
+
+    if (targetComponent != null)
+    {
+      IRegion nodeRegion = getHyperlinkRegion(node);
+
+      return new XSDHyperlink(nodeRegion, targetComponent);
+    }
+
+    return null;
+  }
+
+  /**
+   * Finds the XSD component for the given node.
+   * 
+   * @param xsdSchema cannot be null
+   * @param node cannot be null
+   * @return XSDConcreteComponent
+   */
+  private XSDConcreteComponent getTargetXSDComponent(XSDSchema xsdSchema, IDOMNode node)
+  {
+    XSDConcreteComponent component = null;
+
+    XSDConcreteComponent xsdComp = xsdSchema.getCorrespondingComponent((Node) node);
+    if (xsdComp instanceof XSDElementDeclaration)
+    {
+      XSDElementDeclaration elementDecl = (XSDElementDeclaration) xsdComp;
+      if (elementDecl.isElementDeclarationReference())
+      {
+        component = elementDecl.getResolvedElementDeclaration();
+      }
+      else
+      {
+        XSDConcreteComponent typeDef = null;
+        if (elementDecl.getAnonymousTypeDefinition() == null)
+        {
+          typeDef = elementDecl.getTypeDefinition();
+        }
+
+        XSDConcreteComponent subGroupAffiliation = elementDecl.getSubstitutionGroupAffiliation();
+
+        if (typeDef != null && subGroupAffiliation != null)
+        {
+          // we have 2 things we can navigate to, if the
+          // cursor is anywhere on the substitution
+          // attribute
+          // then jump to that, otherwise just go to the
+          // typeDef.
+          if (node instanceof Attr && ((Attr) node).getLocalName().equals(XSDConstants.SUBSTITUTIONGROUP_ATTRIBUTE))
+          {
+            component = subGroupAffiliation;
+          }
+          else
+          {
+            // try to reveal the type now. On success,
+            // then we return true.
+            // if we fail, set the substitution group
+            // as
+            // the object to reveal as a backup plan.
+            // ISSUE: how to set backup?
+            // if (revealObject(typeDef)) {
+            component = typeDef;
+            // }
+            // else {
+            // objectToReveal = subGroupAffiliation;
+            // }
+          }
+        }
+        else
+        {
+          // one or more of these is null. If the
+          // typeDef is
+          // non-null, use it. Otherwise
+          // try and use the substitution group
+          component = typeDef != null ? typeDef : subGroupAffiliation;
+        }
+      }
+    }
+    else if (xsdComp instanceof XSDModelGroupDefinition)
+    {
+      XSDModelGroupDefinition elementDecl = (XSDModelGroupDefinition) xsdComp;
+      if (elementDecl.isModelGroupDefinitionReference())
+      {
+        component = elementDecl.getResolvedModelGroupDefinition();
+      }
+    }
+    else if (xsdComp instanceof XSDAttributeDeclaration)
+    {
+      XSDAttributeDeclaration attrDecl = (XSDAttributeDeclaration) xsdComp;
+      if (attrDecl.isAttributeDeclarationReference())
+      {
+        component = attrDecl.getResolvedAttributeDeclaration();
+      }
+      else if (attrDecl.getAnonymousTypeDefinition() == null)
+      {
+        component = attrDecl.getTypeDefinition();
+      }
+    }
+    else if (xsdComp instanceof XSDAttributeGroupDefinition)
+    {
+      XSDAttributeGroupDefinition attrGroupDef = (XSDAttributeGroupDefinition) xsdComp;
+      if (attrGroupDef.isAttributeGroupDefinitionReference())
+      {
+        component = attrGroupDef.getResolvedAttributeGroupDefinition();
+      }
+    }
+    else if (xsdComp instanceof XSDIdentityConstraintDefinition)
+    {
+      XSDIdentityConstraintDefinition idConstraintDef = (XSDIdentityConstraintDefinition) xsdComp;
+      if (idConstraintDef.getReferencedKey() != null)
+      {
+        component = idConstraintDef.getReferencedKey();
+      }
+    }
+    else if (xsdComp instanceof XSDSimpleTypeDefinition)
+    {
+      XSDSimpleTypeDefinition typeDef = (XSDSimpleTypeDefinition) xsdComp;
+
+      // Simple types can be one of restriction, list or union.
+
+      XSDVariety variety = typeDef.getVariety();
+      int varietyType = variety.getValue();
+      
+      switch (varietyType)
+      {
+        case XSDVariety.ATOMIC : 
+          {
+            component = typeDef.getBaseTypeDefinition();
+          }
+          break;
+        case XSDVariety.LIST : 
+          {
+            component = typeDef.getItemTypeDefinition();
+          }
+          break;
+        case XSDVariety.UNION : 
+          {
+            List memberTypes = typeDef.getMemberTypeDefinitions();
+            if (memberTypes != null && memberTypes.size() > 0)
+            {
+              // ISSUE: What if there are more than one type?
+              // This could be a case for multiple hyperlinks at the same
+              // location.
+              component = (XSDConcreteComponent) memberTypes.get(0);
+            }
+          }
+          break;
+      }
+    }
+    else if (xsdComp instanceof XSDTypeDefinition)
+    {
+      XSDTypeDefinition typeDef = (XSDTypeDefinition) xsdComp;
+      component = typeDef.getBaseType();
+    }
+    else if (xsdComp instanceof XSDSchemaDirective)
+    {
+      XSDSchemaDirective directive = (XSDSchemaDirective) xsdComp;
+      component = directive.getResolvedSchema();
+    }
+
+    // Avoid types located in the schema for schema (the built in XSD types) 
+    // as we don't want to navigate to their definition.
+    
+    if (component != null)
+    {
+      XSDSchema schema =  component.getSchema();
+
+      if (schema.equals(schema.getSchemaForSchema())) {
+        component = null;
+      }
+    }
+    
+    return component;
+  }
+
+  /**
+   * Gets the xsd schema from document
+   * 
+   * @param document
+   * @return XSDSchema or null of one does not exist yet for document
+   */
+  private XSDSchema getXSDSchema(IDocument document)
+  {
+    XSDSchema schema = null;
+    IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+    if (model != null)
+    {
+      try
+      {
+        if (model instanceof IDOMModel)
+        {
+          IDOMDocument domDoc = ((IDOMModel) model).getDocument();
+          if (domDoc != null)
+          {
+            XSDModelAdapter modelAdapter = (XSDModelAdapter) domDoc.getExistingAdapter(XSDModelAdapter.class);
+            /*
+             * ISSUE: Didn't want to go through initializing schema if it does
+             * not already exist, so just attempted to get existing adapter. If
+             * doesn't exist, just don't bother working.
+             */
+            if (modelAdapter != null)
+              schema = modelAdapter.getSchema();
+          }
+        }
+      }
+      finally
+      {
+        model.releaseFromRead();
+      }
+    }
+    return schema;
+  }
+}