/******************************************************************************* * Copyright (c) 2013 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.el.ui.internal.info; import java.io.IOException; import java.io.PushbackReader; import java.io.Reader; import java.util.HashSet; import java.util.Set; import org.eclipse.jface.internal.text.html.SubstitutionTextReader; /** * Excludes anchors with unknown protocol schema in 'href' attributes * * @author Victor V. Rubezhny * */ public class HTMLAnchorProcessor extends SubstitutionTextReader { private static final String EMPTY_STRING= ""; //$NON-NLS-1$ Set<String> fEnabledProtocols = new HashSet<String>(2); private boolean fIgnoredAnchor= false; public HTMLAnchorProcessor(Reader reader, String[] enabledProtocols) { super(new PushbackReader(reader)); if (enabledProtocols != null) { for (String p : enabledProtocols) { if (p != null && p.trim().length() > 0) fEnabledProtocols.add(p.trim().toLowerCase()); } } } @Override protected String computeSubstitution(int c) throws IOException { if (c == '<') return processHTMLTag(); return null; } /* * A '<' has been read. Process a html tag */ private String processHTMLTag() throws IOException { StringBuffer buf= new StringBuffer(); int ch; do { ch= nextChar(); while (ch != -1 && ch != '>') { buf.append((char) ch); ch= nextChar(); if (ch == '"'){ buf.append((char) ch); ch= nextChar(); while (ch != -1 && ch != '"'){ buf.append((char) ch); ch= nextChar(); } } if (ch == '<' && !isInComment(buf)) { unread(ch); return '<' + buf.toString(); } } if (ch == -1) return null; if (!isInComment(buf) || isCommentEnd(buf)) { break; } // unfinished comment buf.append((char) ch); } while (true); return internalProcessTag(buf.toString()); } private void unread(int ch) throws IOException { ((PushbackReader) getReader()).unread(ch); } private static boolean isInComment(StringBuffer buf) { return buf.length() >= 3 && "!--".equals(buf.substring(0, 3)); //$NON-NLS-1$ } private static boolean isCommentEnd(StringBuffer buf) { int tagLen= buf.length(); return tagLen >= 5 && "--".equals(buf.substring(tagLen - 2)); //$NON-NLS-1$ } private String internalProcessTag(String tagText) { if (tagText == null || tagText.length() == 0) return EMPTY_STRING; String html= tagText.toLowerCase(); String tag= html; if ('/' == tag.charAt(0)) tag= tag.substring(1); if ("a".equals(html) || (html.length() > 2 && html.startsWith("a") && Character.isWhitespace(html.charAt(1)))) { //$NON-NLS-1$ $NON-NLS-2$ return startAnchor(html) ? EMPTY_STRING : '<' + tagText + '>'; } if ("/a".equals(html)) { //$NON-NLS-1$ return stopAnchor() ? EMPTY_STRING : '<' + tagText + '>'; } return '<' + tagText + '>'; } private boolean startAnchor(String html) { fIgnoredAnchor = true; int hrefStart = html.indexOf("href"); //$NON-NLS-1$ if (hrefStart != -1) { int equalStart = html.indexOf('=', hrefStart); if (equalStart != -1) { if ("href".equals(html.substring(hrefStart, equalStart).trim())) { int valueStart = equalStart + 1; while (valueStart < html.length() && !Character.isLetterOrDigit(html.charAt(valueStart)) && ('"' == html.charAt(valueStart) || '\'' == html.charAt(valueStart) || Character.isWhitespace(html.charAt(valueStart)))) { valueStart++; } int end = html.indexOf(':', valueStart); if (end != -1) { String prot = html.substring(valueStart, end); fIgnoredAnchor = !fEnabledProtocols.contains(prot); } } } } return fIgnoredAnchor; } private boolean stopAnchor() { boolean result = fIgnoredAnchor; if (fIgnoredAnchor) { fIgnoredAnchor = false; } return result; } }