/******************************************************************************* * Copyright (c) 2006, 2007 Red Hat, Inc. * 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: * Red Hat Incorporated - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.autotools.ui.text.hover; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.eclipse.cdt.autotools.core.AutotoolsPlugin; import org.eclipse.cdt.autotools.ui.AutotoolsUIPlugin; import org.eclipse.cdt.autotools.ui.editors.AutoconfEditor; import org.eclipse.cdt.autotools.ui.editors.AutoconfMacro; import org.eclipse.cdt.autotools.ui.editors.IAutotoolEditorActionDefinitionIds; import org.eclipse.cdt.internal.autotools.core.AutotoolsPropertyConstants; import org.eclipse.cdt.internal.autotools.ui.CWordFinder; import org.eclipse.cdt.internal.autotools.ui.HTMLPrinter; import org.eclipse.cdt.internal.autotools.ui.HTMLTextPresenter; import org.eclipse.cdt.internal.autotools.ui.preferences.AutotoolsEditorPreferenceConstants; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextHoverExtension; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.keys.IBindingService; import org.osgi.framework.Bundle; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class AutoconfTextHover implements ITextHover, ITextHoverExtension { public static final String LOCAL_AUTOCONF_MACROS_DOC_NAME = "macros/acmacros"; public static final String LOCAL_AUTOMAKE_MACROS_DOC_NAME = "macros/ammacros"; public static final String AUTOCONF_MACROS_DOC_NAME = "http://www.sourceware.org/eclipse/autotools/acmacros"; //$NON-NLS-1$ public static final String AUTOMAKE_MACROS_DOC_NAME = "http://www.sourceware.org/eclipse/autotools/ammacros"; //$NON-NLS-1$ private static class AutotoolsHoverDoc { public Document[] documents = new Document[2]; public AutotoolsHoverDoc(Document ac_document, Document am_document) { this.documents[0] = ac_document; this.documents[1] = am_document; } public Document getAcDocument() { return documents[0]; } public Document getAmDocument() { return documents[1]; } public Document[] getDocuments() { return documents; } }; private static Map<String, Document> acHoverDocs; private static Map<String, Document> amHoverDocs; private static Map<Document, ArrayList<AutoconfMacro>> acHoverMacros; private static String fgStyleSheet; private static AutoconfEditor fEditor; /* Mapping key to action */ private static IBindingService fBindingService; { fBindingService= (IBindingService)PlatformUI.getWorkbench().getAdapter(IBindingService.class); } public static String getAutoconfMacrosDocName(String version) { return AUTOCONF_MACROS_DOC_NAME + "-" //$NON-NLS-1$ + version + ".xml"; //$NON-NLS-1$ } public static String getLocalAutoconfMacrosDocName(String version) { return LOCAL_AUTOCONF_MACROS_DOC_NAME + "-" //$NON-NLS-1$ + version + ".xml"; //$NON-NLS-1$ } /* Get the preferences default for the autoconf macros document name. */ public static String getDefaultAutoconfMacrosVer() { return AutotoolsPlugin.getDefault().getPreferenceStore().getString(AutotoolsEditorPreferenceConstants.AUTOCONF_VERSION); } public static String getAutomakeMacrosDocName(String version) { return AUTOMAKE_MACROS_DOC_NAME + "-" //$NON-NLS-1$ + version + ".xml"; //$NON-NLS-1$ } public static String getLocalAutomakeMacrosDocName(String version) { return LOCAL_AUTOMAKE_MACROS_DOC_NAME + "-" //$NON-NLS-1$ + version + ".xml"; //$NON-NLS-1$ } /* Get the preferences default for the autoconf macros document name. */ public static String getDefaultAutomakeMacrosVer() { return AutotoolsPlugin.getDefault().getPreferenceStore().getString(AutotoolsEditorPreferenceConstants.AUTOMAKE_VERSION); } protected static Document getACDoc(String acDocVer) { Document ac_document = null; if (acHoverDocs == null) { acHoverDocs = new HashMap<String, Document>(); } ac_document = (Document)acHoverDocs.get(acDocVer); if (ac_document == null) { Document doc = null; try { // see comment in initialize() try { InputStream docStream = null; try { URI uri = new URI(getLocalAutoconfMacrosDocName(acDocVer)); IPath p = URIUtil.toPath(uri); // Try to open the file as local to this plug-in. docStream = FileLocator.openStream(AutotoolsUIPlugin.getDefault().getBundle(), p, false); } catch (IOException e) { // Local open failed. Try normal external location. URI acDoc = new URI(getAutoconfMacrosDocName(acDocVer)); IPath p = URIUtil.toPath(acDoc); if (p == null) { URL url = acDoc.toURL(); docStream = url.openStream(); } else { docStream = new FileInputStream(p.toFile()); } } DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); try { DocumentBuilder builder = factory.newDocumentBuilder(); doc = builder.parse(docStream); } catch (SAXParseException saxException) { doc = null; } catch (SAXException saxEx) { doc = null; } catch (ParserConfigurationException pce) { doc = null; } catch (IOException ioe) { doc = null; } finally { if (docStream != null) docStream.close(); } } catch (FileNotFoundException e) { AutotoolsPlugin.log(e); } catch (MalformedURLException e) { AutotoolsPlugin.log(e); } catch (URISyntaxException e) { AutotoolsPlugin.log(e); } ac_document = doc; } catch (IOException ioe) { } } acHoverDocs.put(acDocVer, ac_document); return ac_document; } protected static Document getAMDoc(String amDocVer) { Document am_document = null; if (amHoverDocs == null) { amHoverDocs = new HashMap<String, Document>(); } am_document = (Document)amHoverDocs.get(amDocVer); if (am_document == null) { Document doc = null; try { // see comment in initialize() try { InputStream docStream = null; try { URI uri = new URI(getLocalAutomakeMacrosDocName(amDocVer)); IPath p = URIUtil.toPath(uri); // Try to open the file as local to this plug-in. docStream = FileLocator.openStream(AutotoolsUIPlugin.getDefault().getBundle(), p, false); } catch (IOException e) { // Local open failed. Try normal external location. URI acDoc = new URI(getAutomakeMacrosDocName(amDocVer)); IPath p = URIUtil.toPath(acDoc); if (p == null) { URL url = acDoc.toURL(); docStream = url.openStream(); } else { docStream = new FileInputStream(p.toFile()); } } DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); try { DocumentBuilder builder = factory.newDocumentBuilder(); doc = builder.parse(docStream); } catch (SAXParseException saxException) { doc = null; } catch (SAXException saxEx) { doc = null; } catch (ParserConfigurationException pce) { doc = null; } catch (IOException ioe) { doc = null; } finally { if (docStream != null) docStream.close(); } } catch (FileNotFoundException e) { AutotoolsPlugin.log(e); } catch (MalformedURLException e) { AutotoolsPlugin.log(e); } catch (URISyntaxException e) { AutotoolsPlugin.log(e); } am_document = doc; } catch (IOException ioe) { } } amHoverDocs.put(amDocVer, am_document); return am_document; } protected static AutotoolsHoverDoc getHoverDoc(IEditorInput input) { String acDocVer = getDefaultAutoconfMacrosVer(); String amDocVer = getDefaultAutomakeMacrosVer(); if (input instanceof IFileEditorInput) { IFileEditorInput fe = (IFileEditorInput)input; IFile f = fe.getFile(); IProject p = f.getProject(); try { String acVer = p.getPersistentProperty(AutotoolsPropertyConstants.AUTOCONF_VERSION); if (acVer != null) acDocVer = acVer; else { // look for compat project properties acVer = p.getPersistentProperty(AutotoolsPropertyConstants.AUTOCONF_VERSION_COMPAT); if (acVer != null) acDocVer = acVer; } } catch (CoreException ce1) { // do nothing } try { String amVer = p.getPersistentProperty(AutotoolsPropertyConstants.AUTOMAKE_VERSION); if (amVer != null) amDocVer = amVer; else { // look for compat project properties amVer = p.getPersistentProperty(AutotoolsPropertyConstants.AUTOMAKE_VERSION_COMPAT); if (amVer != null) amDocVer = amVer; } } catch (CoreException ce2) { // do nothing } } Document ac_document = getACDoc(acDocVer); Document am_document = getAMDoc(amDocVer); return new AutoconfTextHover.AutotoolsHoverDoc(ac_document, am_document); } public AutoconfTextHover(AutoconfEditor editor) { fEditor = editor; } public static String getIndexedInfo(String name, AutoconfEditor editor) { AutotoolsHoverDoc h = getHoverDoc(editor.getEditorInput()); String x = getIndexedInfoFromDocument(name, h.getAcDocument()); if (x == null) x = getIndexedInfoFromDocument(name, h.getAmDocument()); return x; } private static String getIndexedInfoFromDocument(String name, Document document) { StringBuffer buffer = new StringBuffer(); if (document != null && name != null) { Element elem = document.getElementById(name); if (null != elem) { int prototypeCount = 0; buffer.append("<B>Macro:</B> " + name); NodeList nl = elem.getChildNodes(); for (int i = 0; i < nl.getLength(); ++i) { Node n = nl.item(i); String nodeName = n.getNodeName(); if (nodeName.equals("prototype")) { //$NON-NLS-1$ StringBuffer prototype = new StringBuffer(); ++prototypeCount; if (prototypeCount == 1) { buffer.append(" ("); } else buffer.append(" <B>or</B> " + name + " (<I>"); //$NON-NLS-2$ NodeList varList = n.getChildNodes(); for (int j = 0; j < varList.getLength(); ++j) { Node v = varList.item(j); String vnodeName = v.getNodeName(); if (vnodeName.equals("parameter")) { //$NON-NLS-1$ NamedNodeMap parms = v.getAttributes(); Node parmNode = parms.item(0); String parm = parmNode.getNodeValue(); if (prototype.toString().equals("")) prototype.append(parm); else prototype.append(", " + parm); } } buffer.append(prototype.toString() + "</I>)<br>"); //$NON-NLS-1$ } if (nodeName.equals("synopsis")) { //$NON-NLS-1$ Node textNode = n.getLastChild(); buffer.append("<br><B>Synopsis:</B> "); buffer.append(textNode.getNodeValue()); } } } } if (buffer.length() > 0) { HTMLPrinter.insertPageProlog(buffer, 0); HTMLPrinter.addPageEpilog(buffer); return buffer.toString(); } return null; } public static AutoconfMacro[] getMacroList(AutoconfEditor editor) { IEditorInput input = editor.getEditorInput(); AutotoolsHoverDoc hoverdoc = getHoverDoc(input); AutoconfMacro[] macros = getMacroList(hoverdoc); return macros; } private static AutoconfMacro[] getMacroList(AutotoolsHoverDoc hoverdoc) { if (acHoverMacros == null) { acHoverMacros = new HashMap<Document, ArrayList<AutoconfMacro>>(); } ArrayList<AutoconfMacro> masterList = new ArrayList<AutoconfMacro>(); Document[] doc = hoverdoc.getDocuments(); for (int ix = 0; ix < doc.length; ++ix) { Document macroDoc = doc[ix]; ArrayList<AutoconfMacro> list = acHoverMacros.get(macroDoc); if (list == null && macroDoc != null) { list = new ArrayList<AutoconfMacro>(); NodeList nl = macroDoc.getElementsByTagName("macro"); //$NON-NLS-1$ for (int i = 0; i < nl.getLength(); ++i) { Node macro = nl.item(i); NamedNodeMap macroAttrs = macro.getAttributes(); Node n2 = macroAttrs.getNamedItem("id"); //$NON-NLS-1$ if (n2 != null) { String name = n2.getNodeValue(); StringBuffer parms = new StringBuffer(); NodeList macroChildren = macro.getChildNodes(); for (int j = 0; j < macroChildren.getLength(); ++j) { Node x = macroChildren.item(j); if (x.getNodeName().equals("prototype")) { //$NON-NLS-1$ // Use parameters for context info. NodeList parmList = x.getChildNodes(); int parmCount = 0; for (int k = 0; k < parmList.getLength(); ++k) { Node n3 = parmList.item(k); if (n3.getNodeName() == "parameter") { //$NON-NLS-1$ NamedNodeMap parmVals = n3.getAttributes(); Node parmVal = parmVals.item(0); if (parmCount > 0) parms = parms.append(", "); //$NON-NLS-1$ parms.append(parmVal.getNodeValue()); ++parmCount; } } } } AutoconfMacro m = new AutoconfMacro(name, parms.toString()); list.add(m); } } // Cache the arraylist of macros for later usage. acHoverMacros.put(macroDoc, list); } if (list != null) masterList.addAll(list); } // Convert to a sorted array of macros and return result. AutoconfMacro[] macros = new AutoconfMacro[masterList.size()]; masterList.toArray(macros); Arrays.sort(macros); return macros; } public static AutoconfPrototype getPrototype(String name, AutoconfEditor editor) { IEditorInput input = editor.getEditorInput(); AutotoolsHoverDoc hoverdoc = getHoverDoc(input); AutoconfPrototype x = getPrototype(name, hoverdoc.getAcDocument()); if (x == null) x = getPrototype(name, hoverdoc.getAmDocument()); return x; } private static AutoconfPrototype getPrototype(String name, Document document) { AutoconfPrototype p = null; if (document != null && name != null) { Element elem = document.getElementById(name); if (null != elem) { int prototypeCount = -1; p = new AutoconfPrototype(); p.setName(name); NodeList nl = elem.getChildNodes(); for (int i = 0; i < nl.getLength(); ++i) { Node n = nl.item(i); String nodeName = n.getNodeName(); if (nodeName.equals("prototype")) { //$NON-NLS-1$ ++prototypeCount; int parmCount = 0; int minParmCount = -1; p.setNumPrototypes(prototypeCount + 1); NodeList varList = n.getChildNodes(); for (int j = 0; j < varList.getLength(); ++j) { Node v = varList.item(j); String vnodeName = v.getNodeName(); if (vnodeName.equals("parameter")) { //$NON-NLS-1$ ++parmCount; NamedNodeMap parms = v.getAttributes(); Node parmNode = parms.item(0); String parm = parmNode.getNodeValue(); // Check for first optional parameter which means // we know the minimum number of parameters needed. if (minParmCount < 0 && (parm.charAt(0) == '[' || parm.startsWith("..."))) minParmCount = parmCount - 1; // Old style documentation sometimes had '[' in // prototypes so look for one at end of a parm too. else if (minParmCount < 0 && parm.endsWith("[")) minParmCount = parmCount; p.setParmName(prototypeCount, parmCount - 1, parm); } } p.setMaxParms(prototypeCount, parmCount); // If we see no evidence of optional parameters, then // the min and max number of parameters are equal. if (minParmCount < 0) minParmCount = parmCount; p.setMinParms(prototypeCount, minParmCount); } } } } return p; } public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { String hoverInfo = null; IDocument d = textViewer.getDocument(); try { String name = d.get(hoverRegion.getOffset(), hoverRegion.getLength()); hoverInfo = getIndexedInfo(name, fEditor); } catch (BadLocationException e) { // do nothing } // TODO Auto-generated method stub return hoverInfo; } /* * @see ITextHover#getHoverRegion(ITextViewer, int) */ public IRegion getHoverRegion(ITextViewer textViewer, int offset) { if (textViewer != null) { /* * If the hover offset falls within the selection range return the * region for the whole selection. */ Point selectedRange = textViewer.getSelectedRange(); if (selectedRange.x >= 0 && selectedRange.y > 0 && offset >= selectedRange.x && offset <= selectedRange.x + selectedRange.y) return new Region(selectedRange.x, selectedRange.y); else { return CWordFinder.findWord(textViewer.getDocument(), offset); } } return null; } /* * @see ITextHoverExtension#getHoverControlCreator() * @since 3.0 */ public IInformationControlCreator getHoverControlCreator() { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, getTooltipAffordanceString(), new HTMLTextPresenter(false)); } }; } /* * Static member function to allow content assist to add hover help. */ public static IInformationControlCreator getInformationControlCreator() { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, getTooltipAffordanceString(), new HTMLTextPresenter(false)); } }; } protected static String getTooltipAffordanceString() { if (fBindingService == null) return null; String keySequence= fBindingService.getBestActiveBindingFormattedFor(IAutotoolEditorActionDefinitionIds.SHOW_TOOLTIP); if (keySequence == null) return null; return HoverMessages.getFormattedString("ToolTipFocus", keySequence); //$NON-NLS-1$ } /** * Returns the style sheet. * * @since 3.2 */ protected static String getStyleSheet() { if (fgStyleSheet == null) { Bundle bundle= Platform.getBundle(AutotoolsUIPlugin.PLUGIN_ID); URL styleSheetURL= bundle.getEntry("/AutoconfHoverStyleSheet.css"); //$NON-NLS-1$ if (styleSheetURL != null) { try { styleSheetURL= FileLocator.toFileURL(styleSheetURL); BufferedReader reader= new BufferedReader(new InputStreamReader(styleSheetURL.openStream())); try { StringBuffer buffer= new StringBuffer(200); String line= reader.readLine(); while (line != null) { buffer.append(line); buffer.append('\n'); line= reader.readLine(); } fgStyleSheet= buffer.toString(); } finally { reader.close(); } } catch (IOException ex) { AutotoolsUIPlugin.log(ex); fgStyleSheet= ""; //$NON-NLS-1$ } } } return fgStyleSheet; } }