blob: 90f616bb701b490195ce289110013aee250362ca [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 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
*******************************************************************************/
package org.eclipse.ant.internal.ui.editor.text;
import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.AbstractFileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.PatternSet;
import org.eclipse.ant.internal.launching.debug.model.AntProperty;
import org.eclipse.ant.internal.launching.debug.model.AntStackFrame;
import org.eclipse.ant.internal.launching.debug.model.AntValue;
import org.eclipse.ant.internal.ui.editor.AntEditor;
import org.eclipse.ant.internal.ui.editor.AntEditorSourceViewerConfiguration;
import org.eclipse.ant.internal.ui.model.AntElementNode;
import org.eclipse.ant.internal.ui.model.AntModel;
import org.eclipse.ant.internal.ui.model.AntPropertyNode;
import org.eclipse.ant.internal.ui.model.IAntModel;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jface.internal.text.html.HTMLPrinter;
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.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.information.IInformationProviderExtension2;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.editors.text.EditorsUI;
public class XMLTextHover implements ITextHover, ITextHoverExtension, IInformationProviderExtension2 {
private AntEditor fEditor;
public XMLTextHover(AntEditor editor) {
super();
fEditor = editor;
}
/*
* Formats a message as HTML text.
* Expects the message to already be properly escaped
*/
private String formatMessage(String message) {
StringBuffer buffer= new StringBuffer();
HTMLPrinter.addPageProlog(buffer);
HTMLPrinter.addParagraph(buffer, message);
HTMLPrinter.addPageEpilog(buffer);
return buffer.toString();
}
/*
* Formats a message as HTML text.
*/
private String formatPathMessage(String[] list) {
StringBuffer buffer= new StringBuffer();
HTMLPrinter.addPageProlog(buffer);
HTMLPrinter.addSmallHeader(buffer, AntEditorTextMessages.XMLTextHover_4);
HTMLPrinter.startBulletList(buffer);
for (int i = 0; i < list.length; i++) {
HTMLPrinter.addBullet(buffer, list[i]);
}
HTMLPrinter.endBulletList(buffer);
HTMLPrinter.addPageEpilog(buffer);
return buffer.toString();
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
*/
public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
if (!(textViewer instanceof ISourceViewer)) {
return null;
}
ISourceViewer sourceViewer= (ISourceViewer) textViewer;
IAnnotationModel model= sourceViewer.getAnnotationModel();
if (model != null) {
String message= getAnnotationModelHoverMessage(model, hoverRegion);
if (message != null) {
return message;
}
}
AntModel antModel= fEditor.getAntModel();
if (antModel == null) { //the ant model has not been created yet
return null;
}
return getAntModelHoverMessage(antModel, hoverRegion, textViewer);
}
private String getAntModelHoverMessage(AntModel antModel, IRegion hoverRegion, ITextViewer textViewer){
try {
IDocument document= textViewer.getDocument();
int offset= hoverRegion.getOffset();
int length= hoverRegion.getLength();
String text= document.get(offset, length);
String value;
AntElementNode node= antModel.getNode(offset, false);
if (document.get(offset - 2, 2).equals("${") || node instanceof AntPropertyNode) { //$NON-NLS-1$
AntStackFrame frame= getFrame();
if (frame != null) {//active Ant debug session
AntProperty property= frame.findProperty(text);
if (property != null) {
return ((AntValue)property.getValue()).getValueString();
}
}
value= antModel.getPropertyValue(text);
if (value != null) {
return formatMessage(HTMLPrinter.convertToHTMLContent(value));
}
}
value= antModel.getTargetDescription(text);
if (value != null) {
return formatMessage(HTMLPrinter.convertToHTMLContent(value));
}
Object referencedObject= antModel.getReferenceObject(text);
if (referencedObject != null) {
if (referencedObject instanceof Path) {
return formatPathMessage(((Path)referencedObject).list());
} else if (referencedObject instanceof PatternSet) {
return formatPatternSetMessage((PatternSet) referencedObject);
} else if (referencedObject instanceof AbstractFileSet) {
return formatFileSetMessage((AbstractFileSet)referencedObject);
}
}
} catch (BadLocationException e) {
} catch (BuildException be) {
return be.getMessage();
}
return null;
}
private String getAnnotationModelHoverMessage(IAnnotationModel model, IRegion hoverRegion) {
Iterator e= model.getAnnotationIterator();
while (e.hasNext()) {
Annotation a= (Annotation) e.next();
if (a instanceof XMLProblemAnnotation) {
Position p= model.getPosition(a);
if (p.overlapsWith(hoverRegion.getOffset(), hoverRegion.getLength())) {
String msg= a.getText();
if (msg != null && msg.trim().length() > 0) {
return formatMessage(msg);
}
}
}
}
return null;
}
private String formatFileSetMessage(AbstractFileSet set) {
FileScanner fileScanner= new FileScanner();
IAntModel antModel= fEditor.getAntModel();
Project project= antModel.getProjectNode().getProject();
set.setupDirectoryScanner(fileScanner, project);
String[] excludedPatterns= fileScanner.getExcludesPatterns();
String[] includesPatterns= fileScanner.getIncludePatterns();
return formatSetMessage(includesPatterns, excludedPatterns);
}
private String formatPatternSetMessage(PatternSet set) {
IAntModel antModel= fEditor.getAntModel();
Project project= antModel.getProjectNode().getProject();
String[] includes= set.getIncludePatterns(project);
String[] excludes= set.getExcludePatterns(project);
return formatSetMessage(includes, excludes);
}
private String formatSetMessage(String[] includes, String[] excludes) {
StringBuffer buffer= new StringBuffer();
HTMLPrinter.addPageProlog(buffer);
if (includes != null && includes.length > 0) {
HTMLPrinter.addSmallHeader(buffer, AntEditorTextMessages.XMLTextHover_5);
for (int i = 0; i < includes.length; i++) {
HTMLPrinter.addBullet(buffer, includes[i]);
}
}
HTMLPrinter.addParagraph(buffer, ""); //$NON-NLS-1$
HTMLPrinter.addParagraph(buffer, ""); //$NON-NLS-1$
if (excludes != null && excludes.length > 0) {
HTMLPrinter.addSmallHeader(buffer, AntEditorTextMessages.XMLTextHover_6);
for (int i = 0; i < excludes.length; i++) {
HTMLPrinter.addBullet(buffer, excludes[i]);
}
}
HTMLPrinter.addPageEpilog(buffer);
return buffer.toString();
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int)
*/
public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
if (textViewer != null) {
return getRegion(textViewer, offset);
}
return null;
}
public static IRegion getRegion(ITextViewer textViewer, int offset) {
IDocument document= textViewer.getDocument();
int start= -1;
int end= -1;
IRegion region= null;
try {
int pos= offset;
char c;
if (document.getChar(pos) == '"') {
pos--;
}
while (pos >= 0) {
c= document.getChar(pos);
if (c != '.' && c != '-' && c != '/' && c != '\\' && c != ' ' && c != ')' && c != '('&& c != ':' && !Character.isJavaIdentifierPart(c) && pos != offset)
break;
--pos;
}
start= pos;
pos= offset;
int length= document.getLength();
while (pos < length) {
c= document.getChar(pos);
if (c != '.' && c != '-' && c != '/' && c != '\\' && c != ' ' && c != ')' && c != '('&& c != ':' && !Character.isJavaIdentifierPart(c))
break;
if (c == '/' && (document.getLength() - 1) > (pos + 1) && document.getChar(pos + 1) == '>') {
//e.g. <name/>
break;
}
++pos;
}
end= pos;
} catch (BadLocationException x) {
}
if (start > -1 && end > -1) {
if (start == offset && end == offset) {
return new Region(offset, 0);
} else if (start == offset) {
return new Region(start, end - start);
} else {
try { //correct for spaces at beginning or end
while(document.getChar(start + 1) == ' ') {
start++;
}
while(document.getChar(end - 1) == ' ') {
end--;
}
} catch (BadLocationException e) {
}
region= new Region(start + 1, end - start - 1);
}
}
if (region != null) {
try {
char c= document.getChar(region.getOffset() - 1);
if (c == '"') {
if (document.get(offset, region.getLength()).indexOf(',') != -1) {
region = cleanRegionForNonProperty(offset, document, region);
}
} else if (c != '{') {
region = cleanRegionForNonProperty(offset, document, region);
}
} catch (BadLocationException e) {
}
}
return region;
}
private static IRegion cleanRegionForNonProperty(int offset, IDocument document, IRegion region) throws BadLocationException {
//do not allow spaces in region that is not a property
String text= document.get(region.getOffset(), region.getLength());
if (text.startsWith("/")) { //$NON-NLS-1$
text= text.substring(1);
region= new Region(region.getOffset() + 1, region.getLength() - 1);
}
StringTokenizer tokenizer= new StringTokenizer(text, " "); //$NON-NLS-1$
if (tokenizer.countTokens() != 1) {
while(tokenizer.hasMoreTokens()) {
String token= tokenizer.nextToken();
int index= text.indexOf(token);
if (region.getOffset() + index <= offset && region.getOffset() + index + token.length() >= offset) {
region= new Region(region.getOffset() + index, token.length());
break;
}
}
}
return region;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.ITextHoverExtension#getHoverControlCreator()
*/
public IInformationControlCreator getHoverControlCreator() {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent, EditorsUI.getTooltipAffordanceString());
}
};
}
/**
* Returns the stack frame in which to search for properties, or <code>null</code>
* if none.
*
* @return the stack frame in which to search for properties, or <code>null</code>
* if none
*/
private AntStackFrame getFrame() {
IAdaptable adaptable = DebugUITools.getDebugContext();
if (adaptable != null) {
return (AntStackFrame)adaptable.getAdapter(AntStackFrame.class);
}
return null;
}
/*
* @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
* @since 3.3
*/
public IInformationControlCreator getInformationPresenterControlCreator() {
return AntEditorSourceViewerConfiguration.getInformationPresenterControlCreator();
}
}