/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.editors.unified; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationHover; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ISourceViewer; /** * UnifiedAnnotationHover * * @author Ingo Muschenetz */ public class UnifiedAnnotationHover implements IAnnotationHover { /** * UnifiedAnnotationHover */ public UnifiedAnnotationHover() { } /** * Returns the distance to the ruler line. */ private int compareRulerLine(Position position, IDocument document, int line) { if (position.getOffset() > -1 && position.getLength() > -1) { try { int xmlAnnotationLine = document.getLineOfOffset(position.getOffset()); if (line == xmlAnnotationLine) { return 1; } if (xmlAnnotationLine <= line && line <= document.getLineOfOffset(position.getOffset() + position.getLength())) { return 2; } } catch (BadLocationException x) { } } return 0; } /** * Returns one marker which includes the ruler's line of activity. */ private List getXMLAnnotationsForLine(ISourceViewer viewer, int line) { IDocument document = viewer.getDocument(); IAnnotationModel model = viewer.getAnnotationModel(); if (model == null) { return null; } List exact = new ArrayList(); Iterator e = model.getAnnotationIterator(); Map messagesAtPosition = new HashMap(); while (e.hasNext()) { Object o = e.next(); if (o instanceof Annotation) { Annotation a = (Annotation) o; Position position = model.getPosition(a); if (position == null) { continue; } if (isDuplicateXMLAnnotation(messagesAtPosition, position, a.getText())) { continue; } switch (compareRulerLine(position, document, line)) { case 1: exact.add(a); break; default: break; } } } return exact; } /** * isDuplicateXMLAnnotation * * @param messagesAtPosition * @param position * @param message * @return boolean */ private boolean isDuplicateXMLAnnotation(Map messagesAtPosition, Position position, String message) { if (messagesAtPosition.containsKey(position)) { Object value = messagesAtPosition.get(position); if (message.equals(value)) { return true; } if (value instanceof List) { List messages = (List) value; if (messages.contains(message)) { return true; } messages.add(message); } else { ArrayList messages = new ArrayList(); messages.add(value); messages.add(message); messagesAtPosition.put(position, messages); } } else { messagesAtPosition.put(position, message); } return false; } /** * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, int) */ public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) { List xmlAnnotations = getXMLAnnotationsForLine(sourceViewer, lineNumber); if (xmlAnnotations != null) { if (xmlAnnotations.size() == 1) { // optimization Annotation xmlAnnotation = (Annotation) xmlAnnotations.get(0); String message = xmlAnnotation.getText(); if (message != null && message.trim().length() > 0) { return formatSingleMessage(message); } } else { List messages = new ArrayList(xmlAnnotations.size()); Iterator e = xmlAnnotations.iterator(); while (e.hasNext()) { Annotation xmlAnnotation = (Annotation) e.next(); String message = xmlAnnotation.getText(); if (message != null && message.trim().length() > 0) { messages.add(message.trim()); } } if (messages.size() == 1) { return formatSingleMessage((String) messages.get(0)); } if (messages.size() > 1) { return formatMultipleMessages(messages); } } } return null; } /** * Formats a message as HTML text. */ private String formatSingleMessage(String message) { StringBuffer buffer = new StringBuffer(); HTMLPrinter.addPageProlog(buffer); HTMLPrinter.addParagraph(buffer, HTMLPrinter.convertToHTMLContent(message)); HTMLPrinter.addPageEpilog(buffer); return buffer.toString(); } /** * Formats several message as HTML text. */ private String formatMultipleMessages(List messages) { StringBuffer buffer = new StringBuffer(); HTMLPrinter.addPageProlog(buffer); HTMLPrinter.addParagraph(buffer, HTMLPrinter.convertToHTMLContent("Multiple items at this position:")); //$NON-NLS-1$ HTMLPrinter.startBulletList(buffer); Iterator e = messages.iterator(); while (e.hasNext()) { HTMLPrinter.addBullet(buffer, HTMLPrinter.convertToHTMLContent((String) e.next())); } HTMLPrinter.endBulletList(buffer); HTMLPrinter.addPageEpilog(buffer); return buffer.toString(); } /** * HTMLPrinter * * @author Ingo Muschenetz */ static final class HTMLPrinter { /** * HTMLPrinter */ private HTMLPrinter() { } /** * replace * * @param text * @param c * @param s * @return String */ private static String replace(String text, char c, String s) { int previous = 0; int current = text.indexOf(c, previous); if (current == -1) { return text; } StringBuffer buffer = new StringBuffer(); while (current > -1) { buffer.append(text.substring(previous, current)); buffer.append(s); previous = current + 1; current = text.indexOf(c, previous); } buffer.append(text.substring(previous)); return buffer.toString(); } /** * convertToHTMLContent * * @param content * @return String */ public static String convertToHTMLContent(String content) { content = replace(content, '<', "<"); //$NON-NLS-1$ return replace(content, '>', ">"); //$NON-NLS-1$ } /** * read * * @param rd * @return String */ public static String read(Reader rd) { StringBuffer buffer = new StringBuffer(); char[] readBuffer = new char[2048]; try { int n = rd.read(readBuffer); while (n > 0) { buffer.append(readBuffer, 0, n); n = rd.read(readBuffer); } return buffer.toString(); } catch (IOException x) { } return null; } /** * insertPageProlog * * @param buffer * @param position */ public static void insertPageProlog(StringBuffer buffer, int position) { buffer.insert(position, "<html><body text=\"#000000\" bgcolor=\"#FFFF88\"><font size=-1>"); //$NON-NLS-1$ } /** * addPageProlog * * @param buffer */ public static void addPageProlog(StringBuffer buffer) { insertPageProlog(buffer, buffer.length()); } /** * addPageEpilog * * @param buffer */ public static void addPageEpilog(StringBuffer buffer) { buffer.append("</font></body></html>"); //$NON-NLS-1$ } /** * startBulletList * * @param buffer */ public static void startBulletList(StringBuffer buffer) { buffer.append("<ul>"); //$NON-NLS-1$ } /** * endBulletList * * @param buffer */ public static void endBulletList(StringBuffer buffer) { buffer.append("</ul>"); //$NON-NLS-1$ } /** * addBullet * * @param buffer * @param bullet */ public static void addBullet(StringBuffer buffer, String bullet) { if (bullet != null) { buffer.append("<li>"); //$NON-NLS-1$ buffer.append(bullet); buffer.append("</li>"); //$NON-NLS-1$ } } /** * addSmallHeader * * @param buffer * @param header */ public static void addSmallHeader(StringBuffer buffer, String header) { if (header != null) { buffer.append("<h5>"); //$NON-NLS-1$ buffer.append(header); buffer.append("</h5>"); //$NON-NLS-1$ } } /** * addParagraph * * @param buffer * @param paragraph */ public static void addParagraph(StringBuffer buffer, String paragraph) { if (paragraph != null) { buffer.append("<p>"); //$NON-NLS-1$ buffer.append(paragraph); } } /** * addParagraph * * @param buffer * @param paragraphReader */ public static void addParagraph(StringBuffer buffer, Reader paragraphReader) { if (paragraphReader != null) { addParagraph(buffer, read(paragraphReader)); } } } }