/* * Copyright (c) 2009 Borland Software Corporation * * 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: * Artem Tikhomirov (Borland) - initial API and implementation */ package org.eclipse.gmf.internal.xpand.inactive; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.content.ITextContentDescriber; /** * Major difference from platform's default content describer (which uses BOM) is that we * supply ISO-8859-1 encoding for legacy template files (those with 0xAB and 0xBB), but keep * exposing UTF-8 as our default encoding. Besides, we do recognize UTF-8 encoding even if * there's no BOM in the file (looking for 0xC2 0xAB and 0xC2 0xBB sequences) * * Another important aspect of this detector is that it is confident (i.e. VALID) about GMF-Xpand * content type when there's no IMPORT statement in the template that looks like OAW Xpand. * @author artem */ public class ContentDescriber implements ITextContentDescriber { private final Pattern myImportClause; public ContentDescriber() { myImportClause = Pattern.compile("IMPORT\\s+"); } public int describe(Reader contents, IContentDescription description) throws IOException { // no idea what I can tell here, but if I do not implement ITextContentDescriber, attempt to save // existing! UTF-8 xpt file under template folder with ISO encoding results in error "can't convert UTF to ISO" // - for some stupid reason Eclipse TextEditor tries to come up with new encoding for existing file final char[] lookahead = new char[1024]; int count = contents.read(lookahead); if (count == 0 || count == -1) { return INDETERMINATE; } return checkSpecificToGMF(CharBuffer.wrap(lookahead, 0, count)); } public int describe(InputStream contents, IContentDescription description) throws IOException { contents = StreamDecoder.ensureMarkSupported(contents); int check = INDETERMINATE; try { final char[] lookahead = new char[1024]; contents.mark(lookahead.length + 1); // use US-ASCII as keywords we are looking for got codes < 127 int count = new InputStreamReader(contents, Charset.forName("US-ASCII")).read(lookahead); //$NON-NLS-1$ if (count == 0 || count == -1) { return INDETERMINATE; } check = checkSpecificToGMF(CharBuffer.wrap(lookahead, 0, count)); if (check == INVALID) { return INVALID; } } finally { contents.reset(); } StreamDecoder sd = new StreamDecoder(contents, null); if (sd.getEncoding() == null) { return check; } if (description != null) { description.setProperty(IContentDescription.CHARSET, sd.getEncoding().name()); } // XXX alternatively, may return 'check' result, but which content type // would get assigned to empty (just created) files then? return VALID; } public QualifiedName[] getSupportedOptions() { return null; // none } // uses INVALID, VALID and INDETERMINATE constants to indicate // whether template is "definitely not GMF's", "definitely GMF's" and "can't tell" respectively private int checkSpecificToGMF(CharSequence s) throws IOException { Matcher m = myImportClause.matcher(s); if (m.find()) { if (m.hitEnd() || m.end() >= s.length()) { return INDETERMINATE; // found IMPORT, but can't tell what's after that } char firstNonSpace = s.charAt(m.end()); return firstNonSpace == '"' || firstNonSpace == '\'' ? VALID : INVALID; } return INDETERMINATE; } }