| /******************************************************************************* |
| * Copyright (c) 2010 Oracle 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: |
| * Oracle Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsf.core.internal.contenttype; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.nio.charset.Charset; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExecutableExtension; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.content.IContentDescription; |
| import org.eclipse.core.runtime.content.ITextContentDescriber; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| import org.eclipse.wst.sse.core.utils.StringUtils; |
| import org.eclipse.wst.xml.core.internal.parser.XMLTokenizer; |
| import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; |
| |
| /** |
| * Matches the first tag of the content using attribute/value regex pairs. |
| * <br/> |
| * <br/> |
| * Example:<br/> |
| * <describer class="XMLDocumentElementAttributeContentDescriber"><br/> |
| * <parameter name="xmlns:ns1" value="http://example.com/ns1" /><br/> |
| * <parameter name="xmlns:.*" value="http://example.com/ns2" /><br/> |
| * </describer> |
| */ |
| public final class XMLDocumentElementAttributeContentDescriber |
| implements ITextContentDescriber, IExecutableExtension { |
| |
| private static class AttributeValuePatternPair { |
| Pattern attribute; |
| Pattern value; |
| |
| public AttributeValuePatternPair(Pattern attribute, Pattern value) { |
| super(); |
| this.attribute = attribute; |
| this.value = value; |
| } |
| |
| public String toString() { |
| return attribute + " : " + value; //$NON-NLS-1$ |
| } |
| } |
| |
| private static final String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$ |
| |
| // TODO: support all of the options of the standard HTML content describer? |
| private final static QualifiedName[] SUPPORTED_OPTIONS = {IContentDescription.BYTE_ORDER_MARK}; |
| |
| /* |
| * Reads from from the stream. Note that the stream will not be repositioned when the method |
| * returns. Copied from org.eclipse.core.internal.content.Util |
| */ |
| private static byte[] getByteOrderMark(InputStream input) throws IOException { |
| int first = input.read(); |
| if (first == 0xEF) { |
| // look for the UTF-8 Byte Order Mark (BOM) |
| int second = input.read(); |
| int third = input.read(); |
| if (second == 0xBB && third == 0xBF) |
| return IContentDescription.BOM_UTF_8; |
| } |
| else if (first == 0xFE) { |
| // look for the UTF-16 BOM |
| if (input.read() == 0xFF) |
| return IContentDescription.BOM_UTF_16BE; |
| } |
| else if (first == 0xFF) { |
| if (input.read() == 0xFE) |
| return IContentDescription.BOM_UTF_16LE; |
| } |
| return null; |
| } |
| |
| private AttributeValuePatternPair[] fPairs = null; |
| |
| /** |
| * Create an instance. |
| */ |
| public XMLDocumentElementAttributeContentDescriber() { |
| super(); |
| } |
| |
| private int checkCriteria(Reader contents) throws IOException { |
| if (fPairs == null || fPairs.length == 0) |
| return INVALID; |
| |
| String attributeName = null; |
| String attributeValue = null; |
| |
| XMLTokenizer tokenizer = new XMLTokenizer(contents); |
| |
| String token = tokenizer.primGetNextToken(); |
| while (token != null && |
| !DOMRegionContext.XML_TAG_CLOSE.equals(token) && |
| !DOMRegionContext.XML_EMPTY_TAG_CLOSE.equals(token)) { |
| |
| if (DOMRegionContext.XML_TAG_ATTRIBUTE_NAME.equals(token)) { |
| attributeName = tokenizer.yytext(); |
| } |
| else if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(token)) { |
| attributeValue = StringUtils.strip(tokenizer.yytext()); |
| for (int i = 0; i < fPairs.length; i++) { |
| boolean nameMatched = fPairs[i].attribute.matcher(attributeName).matches(); |
| if(nameMatched) { |
| boolean valueMatched = fPairs[i].value.matcher(attributeValue).matches(); |
| if(valueMatched) { |
| return VALID; |
| } |
| } |
| } |
| } |
| token = tokenizer.primGetNextToken(); |
| } |
| return INVALID; |
| } |
| |
| public int describe(InputStream contents, IContentDescription description) throws IOException { |
| contents.reset(); |
| byte[] bom = getByteOrderMark(contents); |
| if (bom != null && description != null) |
| description.setProperty(IContentDescription.BYTE_ORDER_MARK, bom); |
| |
| Object value = |
| description != null ? description.getProperty(IContentDescription.CHARSET) : null; |
| String charsetName = null; |
| if (value != null) { |
| charsetName = value.toString(); |
| } |
| else { |
| if (Arrays.equals(IContentDescription.BOM_UTF_16BE, bom)) { |
| charsetName = "UTF-16BE"; //$NON-NLS-1$ |
| } |
| else if (Arrays.equals(IContentDescription.BOM_UTF_16LE, bom)) { |
| charsetName = "UTF-16LE"; //$NON-NLS-1$ |
| } |
| else { |
| charsetName = "UTF-8"; //$NON-NLS-1$ |
| } |
| } |
| contents.reset(); |
| // Check to see if we matched our criteria. |
| return checkCriteria(new InputStreamReader(contents, Charset.forName(charsetName))); |
| } |
| |
| public int describe(Reader contents, IContentDescription description) throws IOException { |
| contents.reset(); |
| // Check to see if we matched our criteria. |
| return checkCriteria(contents); |
| } |
| |
| public QualifiedName[] getSupportedOptions() { |
| return SUPPORTED_OPTIONS; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public void setInitializationData( |
| final IConfigurationElement config, |
| final String propertyName, |
| final Object data) throws CoreException { |
| |
| if (data instanceof String) { |
| // TODO: why is this here? (imported verbatim from bug attachment) |
| } |
| else if (data instanceof Hashtable) { |
| IConfigurationElement describerElement = config.getChildren("describer")[0]; //$NON-NLS-1$ |
| IConfigurationElement[] params = describerElement.getChildren("parameter"); //$NON-NLS-1$ |
| List<AttributeValuePatternPair> pairs = new ArrayList<AttributeValuePatternPair>(); |
| for (int i = 0; i < params.length; i++) { |
| String rawName = params[i].getAttribute(ATTRIBUTE_NAME); |
| String rawValue = params[i].getAttribute(ATTRIBUTE_VALUE); |
| if (rawName == null || rawValue == null) |
| continue; |
| Pattern attributeName = Pattern.compile(rawName); |
| Pattern attributeValue = Pattern.compile(rawValue); |
| pairs.add(new AttributeValuePatternPair(attributeName, attributeValue)); |
| } |
| fPairs = pairs.toArray(new AttributeValuePatternPair[pairs.size()]); |
| } |
| |
| if (fPairs == null || fPairs.length == 0) { |
| throw new CoreException( |
| new Status(IStatus.ERROR, JSFCorePlugin.PLUGIN_ID, 0, |
| "Attributes not specified", null)); //$NON-NLS-1$ |
| } |
| } |
| } |