Initial contents for R3_3_2_pathces from R3_3_maintenance
diff --git a/bundles/org.eclipse.wst.sse.core/.classpath b/bundles/org.eclipse.wst.sse.core/.classpath new file mode 100644 index 0000000..df9dea9 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.classpath
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src-encoding"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="src-tasktags"/> + <classpathentry kind="src" path="src-contentproperties"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"> + <accessrules> + <accessrule kind="accessible" pattern="org/w3c/dom/traversal/NodeIterator"/> + </accessrules> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath>
diff --git a/bundles/org.eclipse.wst.sse.core/.cvsignore b/bundles/org.eclipse.wst.sse.core/.cvsignore new file mode 100644 index 0000000..a71230f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.cvsignore
@@ -0,0 +1,9 @@ +bin +model.jar +build.xml +temp.folder +org.eclipse.wst.sse.core_1.0.0.jar +@dot +src.zip +.fbwarnings +javaCompiler...args
diff --git a/bundles/org.eclipse.wst.sse.core/.options b/bundles/org.eclipse.wst.sse.core/.options new file mode 100644 index 0000000..50b0b16 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.options
@@ -0,0 +1,19 @@ +org.eclipse.wst.sse.core/debug=true +org.eclipse.wst.sse.core/dom/adapter/notification/time=false +org.eclipse.wst.sse.core/dom/adapter/notification/time/criteria=10 +org.eclipse.wst.sse.core/structureddocument=false +org.eclipse.wst.sse.core/filebuffers/modelmanagement=false +org.eclipse.wst.sse.core/filebuffers/leaks=true +org.eclipse.wst.sse.core/filebuffers/lifecycle=false +org.eclipse.wst.sse.core/structuredmodel/lifecycle=false +org.eclipse.wst.sse.core/structuredmodel/state=false +org.eclipse.wst.sse.core/structuredmodel/locks=true +org.eclipse.wst.sse.core/structuredmodel/modelmanager=false +org.eclipse.wst.sse.core/tasks=false +org.eclipse.wst.sse.core/tasks/detection=false +org.eclipse.wst.sse.core/tasks/job=false +org.eclipse.wst.sse.core/tasks/overalltime=false +org.eclipse.wst.sse.core/tasks/time=false +org.eclipse.wst.sse.core/tasks/preferences=false +org.eclipse.wst.sse.core/tasks/registry=false +org.eclipse.wst.sse.core/format=false
diff --git a/bundles/org.eclipse.wst.sse.core/.project b/bundles/org.eclipse.wst.sse.core/.project new file mode 100644 index 0000000..36c5b34 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.project
@@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.wst.sse.core</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription>
diff --git a/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.core.resources.prefs b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..afa5c91 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@ +#Tue Apr 04 03:36:32 EDT 2006 +eclipse.preferences.version=1 +encoding/<project>=ISO-8859-1
diff --git a/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.core.runtime.prefs b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000..7ec5750 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@ +#Mon Apr 17 01:48:39 EDT 2006 +eclipse.preferences.version=1 +line.separator=\r\n
diff --git a/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..104e60d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,80 @@ +#Sat Mar 24 11:23:54 EDT 2007 +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=ignore +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=ignore +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=enabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=ignore +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=error +org.eclipse.jdt.core.compiler.problem.unusedLocal=error +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.3 +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..301c7ad --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,4 @@ +#Mon Apr 17 01:48:39 EDT 2006 +eclipse.preferences.version=1 +internal.default.compliance=default +org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?>\r\n<templates/>
diff --git a/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.ltk.core.refactoring.prefs b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..c59368c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.ltk.core.refactoring.prefs
@@ -0,0 +1,3 @@ +#Tue Apr 04 03:36:32 EDT 2006 +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
diff --git a/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.pde.prefs b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.pde.prefs new file mode 100644 index 0000000..fc522bb --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,16 @@ +#Mon Apr 17 02:01:33 EDT 2006 +compilers.incompatible-environment=0 +compilers.p.build=0 +compilers.p.deprecated=1 +compilers.p.illegal-att-value=0 +compilers.p.no-required-att=0 +compilers.p.not-externalized-att=0 +compilers.p.unknown-attribute=0 +compilers.p.unknown-class=0 +compilers.p.unknown-element=0 +compilers.p.unknown-resource=0 +compilers.p.unresolved-ex-points=0 +compilers.p.unresolved-import=0 +compilers.p.unused-element-or-attribute=0 +compilers.use-project=true +eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.jFlex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.jFlex new file mode 100644 index 0000000..bd7272a --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.jFlex
@@ -0,0 +1,279 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 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 + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.css.core.internal.contenttype; +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.wst.xml.core.internal.contenttype.EncodingParserConstants; +import org.eclipse.wst.xml.core.internal.contenttype.XMLHeadTokenizerConstants; + + + +%% + +%{ + + + private boolean hasMore = true; + private final static int MAX_TO_SCAN = 8000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + + + + public CSSHeadTokenizer() { + super(); + } + + public void reset (Reader in) { + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start + * of the matched text + */ + yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning + * of a line + */ + yy_atBOL = false; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + fStateStack.clear(); + + hasMore = true; + + // its a little wasteful to "throw away" first char array generated + // by class init (via auto generated code), but we really do want + // a small buffer for our head parsers. + if (yy_buffer.length != MAX_TO_SCAN) { + yy_buffer = new char[MAX_TO_SCAN]; + } + + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + context = primGetNextToken(); + HeadParserToken result = null; + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + + private void popState() { + yybegin(fStateStack.pop()); + } + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + + +%} + +%eof{ + hasMore=false; +%eof} + +%public +%class CSSHeadTokenizer +%function primGetNextToken +%type String +%char +%unicode +%ignorecase +%debug +%switch + + +UTF16BE = \xFE\xFF +UTF16LE = \xFF\xFE +UTF83ByteBOM = \xEF\xBB\xBF + +//SpaceChar = [\x20\x09] + + + +// [3] S ::= (0x20 | 0x9 | 0xD | 0xA)+ +S = [\x20\x09\x0D\x0A] + +//BeginAttribeValue = {S}* \= {S}* + +LineTerminator = \r|\n + +// Z is the single-byte zero character to be used in parsing double-byte files +Z = (\x00)? +S_UTF = {Z}{S}{Z} +BeginAttributeValueUTF = {S_UTF}* \= {S_UTF}* + +%state ST_XMLDecl +%state CHARSET_RULE +%state QuotedAttributeValue +%state DQ_STRING +%state SQ_STRING +%state UnDelimitedString + +%% + + +<YYINITIAL> +{ + {UTF16BE} {hasMore = false; return EncodingParserConstants.UTF16BE;} + {UTF16LE} {hasMore = false; return EncodingParserConstants.UTF16LE;} + {UTF83ByteBOM} {hasMore = false; return EncodingParserConstants.UTF83ByteBOM;} + + // force to be started on first line, but we do allow preceeding spaces + ^ {Z}({S}{Z})* ({Z}<{Z}\?{Z}x{Z}m{Z}l{Z}){S_UTF}+ {if (yychar == 0 ) {yybegin(ST_XMLDecl); return XMLHeadTokenizerConstants.XMLDeclStart;}} + + ^ {Z}({S}{Z})*({Z}@{Z}c{Z}h{Z}a{Z}r{Z}s{Z}e{Z}t{Z}) {if (yychar == 0 ) {yybegin(CHARSET_RULE); return CSSHeadTokenizerConstants.CHARSET_RULE;}} +} + +// I don't think there's really an XML form of CSS files ... but will leave here for consistency +<ST_XMLDecl> +{ + //"version" {BeginAttribeValue} {pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDeclVersion;} + {Z}e{Z}n{Z}c{Z}o{Z}d{Z}i{Z}n{Z}g{Z} {BeginAttributeValueUTF} {pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDelEncoding;} + // note this "forced end" once end of XML Declaration found + {Z}\?{Z}>{Z} {yybegin(YYINITIAL); return XMLHeadTokenizerConstants.XMLDeclEnd;} +} + +<CHARSET_RULE> +{ + + {S_UTF}* {pushCurrentState(); yybegin(QuotedAttributeValue);} + {Z};{Z} { yybegin(YYINITIAL); hasMore = false; return CSSHeadTokenizerConstants.RuleEnd;} +} + + +<QuotedAttributeValue> +{ + {Z}\"{Z} { yybegin(DQ_STRING); string.setLength(0); } + {Z}\'{Z} { yybegin(SQ_STRING); string.setLength(0); } + // in this state, anything other than a space character can start an undelimited string + {S_UTF}*. { yypushback(yylength()); yybegin(UnDelimitedString); string.setLength(0);} + +} + + +<DQ_STRING> +{ + + {Z}\"{Z} { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + {Z}{LineTerminator}{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\?{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}<{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + + {Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\/{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z};{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + [^\x00] { string.append( yytext() ); } + + +} + +<SQ_STRING> +{ + + {Z}\'{Z} { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue;} + {Z}{LineTerminator}{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}%{Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}<{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\/{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z};{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + [^\x00] { string.append( yytext() ); } + +} + +<UnDelimitedString> +{ + + + {S_UTF} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + {Z}{LineTerminator}{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\?{Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}<{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + // these are a bit special, since we started an undelimit string, but found a quote ... probably indicates a missing beginning quote + {Z}\'{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue;} + {Z}\"{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue;} + + {Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\/{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z};{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + [^\x00] { string.append( yytext() ); } +} + +// The "match anything" rule should always be in effect except for when looking for end of string +// (That is, remember to update state list if/when new states added) +.|\n {if(yychar > MAX_TO_SCAN) {hasMore=false; return EncodingParserConstants.MAX_CHARS_REACHED;}} + +// this rule always in effect +<<EOF>> {hasMore = false; return EncodingParserConstants.EOF;}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.java b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.java new file mode 100644 index 0000000..d2309dd --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.java
@@ -0,0 +1,1441 @@ +/* The following code was generated by JFlex 1.2.2 on 7/28/08 5:19 PM */ + +/******************************************************************************* + * Copyright (c) 2004, 2008 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 + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.css.core.internal.contenttype; +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.wst.xml.core.internal.contenttype.EncodingParserConstants; +import org.eclipse.wst.xml.core.internal.contenttype.XMLHeadTokenizerConstants; + + + + +/** + * This class is a scanner generated by + * <a href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex</a> 1.2.2 + * on 7/28/08 5:19 PM from the specification file + * <tt>file:/D:/workspaces/wtp301/workspace/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizer.jFlex</tt> + */ +public class CSSHeadTokenizer { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int YYINITIAL = 0; + final public static int UnDelimitedString = 12; + final public static int DQ_STRING = 8; + final public static int SQ_STRING = 10; + final public static int ST_XMLDecl = 2; + final public static int QuotedAttributeValue = 6; + final public static int CHARSET_RULE = 4; + + /** + * YY_LEXSTATE[l] is the state in the DFA for the lexical state l + * YY_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private final static int YY_LEXSTATE[] = { + 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 + }; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = + "\1\11\10\0\1\6\1\10\2\0\1\7\22\0\1\6\1\0\1\37"+ + "\2\0\1\42\1\0\1\40\7\0\1\41\13\0\1\36\1\13\1\12"+ + "\1\35\1\14\1\20\1\23\1\0\1\21\1\32\1\26\1\0\1\34"+ + "\1\22\1\33\2\0\1\17\1\16\1\30\1\31\2\0\1\24\1\25"+ + "\1\27\3\0\1\15\10\0\1\23\1\0\1\21\1\32\1\26\1\0"+ + "\1\34\1\22\1\33\2\0\1\17\1\16\1\30\1\31\2\0\1\24"+ + "\1\25\1\27\3\0\1\15\102\0\1\4\3\0\1\5\17\0\1\3"+ + "\16\0\1\1\20\0\1\3\16\0\1\1\1\2\170\0\1\2\ufe87\0"; + + /** + * Translates characters to character classes + */ + final private static char [] yycmap = yy_unpack_cmap(yycmap_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", + "Internal error: unknown state", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + + + private boolean hasMore = true; + private final static int MAX_TO_SCAN = 8000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + + + + public CSSHeadTokenizer() { + super(); + } + + public void reset (Reader in) { + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start + * of the matched text + */ + yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning + * of a line + */ + yy_atBOL = false; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + fStateStack.clear(); + + hasMore = true; + + // its a little wasteful to "throw away" first char array generated + // by class init (via auto generated code), but we really do want + // a small buffer for our head parsers. + if (yy_buffer.length != MAX_TO_SCAN) { + yy_buffer = new char[MAX_TO_SCAN]; + } + + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + context = primGetNextToken(); + HeadParserToken result = null; + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + + private void popState() { + yybegin(fStateStack.pop()); + } + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + + + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + public CSSHeadTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public CSSHeadTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] yy_unpack_cmap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 160) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning. + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + System.out.println(YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println(YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + + System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + private void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + hasMore=false; + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + + while (true) { + + yychar+= yylength(); + + yy_atBOL = yy_markedPos <= 0 || yy_buffer[yy_markedPos-1] == '\n'; + if (!yy_atBOL && yy_buffer[yy_markedPos-1] == '\r') { + yy_atBOL = yy_advance() != '\n'; + if (!yy_atEOF) yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + if (yy_atBOL) + yy_state = YY_LEXSTATE[yy_lexical_state+1]; + else + yy_state = YY_LEXSTATE[yy_lexical_state]; + + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + + yy_input = yycmap[yy_input]; + + boolean yy_isFinal = false; + boolean yy_noLookAhead = false; + + yy_forNext: { switch (yy_state) { + case 0: + switch (yy_input) { + case 1: yy_isFinal = true; yy_state = 9; break yy_forNext; + case 2: yy_isFinal = true; yy_state = 10; break yy_forNext; + case 3: yy_isFinal = true; yy_state = 11; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 1: + switch (yy_input) { + case 1: yy_isFinal = true; yy_state = 9; break yy_forNext; + case 2: yy_isFinal = true; yy_state = 10; break yy_forNext; + case 3: yy_isFinal = true; yy_state = 11; break yy_forNext; + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 12; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 13; break yy_forNext; + case 11: yy_isFinal = true; yy_state = 14; break yy_forNext; + case 16: yy_isFinal = true; yy_state = 15; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 2: + switch (yy_input) { + case 9: yy_isFinal = true; yy_state = 16; break yy_forNext; + case 12: yy_isFinal = true; yy_state = 17; break yy_forNext; + case 22: yy_isFinal = true; yy_state = 18; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 3: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 19; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 20; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 21; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 4: + switch (yy_input) { + case 6: + case 7: yy_isFinal = true; yy_state = 23; break yy_forNext; + case 8: yy_isFinal = true; yy_state = 24; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 25; break yy_forNext; + case 31: yy_isFinal = true; yy_state = 26; break yy_forNext; + case 32: yy_isFinal = true; yy_state = 27; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 22; break yy_forNext; + } + + case 5: + switch (yy_input) { + case 7: + case 8: + case 11: + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 30; break yy_forNext; + case 12: + case 33: yy_isFinal = true; yy_state = 31; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 32; break yy_forNext; + case 31: yy_isFinal = true; yy_state = 33; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 28; break yy_forNext; + } + + case 6: + switch (yy_input) { + case 7: + case 8: + case 11: + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 33: yy_isFinal = true; yy_state = 31; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 32; break yy_forNext; + case 32: yy_isFinal = true; yy_state = 33; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 34; break yy_forNext; + case 34: yy_isFinal = true; yy_state = 35; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 28; break yy_forNext; + } + + case 7: + switch (yy_input) { + case 11: + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 33: yy_isFinal = true; yy_state = 31; break yy_forNext; + case 12: yy_isFinal = true; yy_state = 35; break yy_forNext; + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 36; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 37; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 38; break yy_forNext; + case 31: + case 32: yy_isFinal = true; yy_state = 39; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 28; break yy_forNext; + } + + case 9: + switch (yy_input) { + case 2: yy_isFinal = true; yy_noLookAhead = true; yy_state = 40; break yy_forNext; + default: break yy_forAction; + } + + case 10: + switch (yy_input) { + case 1: yy_isFinal = true; yy_noLookAhead = true; yy_state = 41; break yy_forNext; + default: break yy_forAction; + } + + case 11: + switch (yy_input) { + case 4: yy_state = 42; break yy_forNext; + default: break yy_forAction; + } + + case 12: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 43; break yy_forNext; + case 9: yy_state = 44; break yy_forNext; + case 11: yy_state = 45; break yy_forNext; + case 16: yy_state = 46; break yy_forNext; + default: break yy_forAction; + } + + case 13: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 43; break yy_forNext; + case 11: yy_state = 45; break yy_forNext; + case 16: yy_state = 46; break yy_forNext; + case 9: yy_state = 47; break yy_forNext; + default: break yy_forAction; + } + + case 14: + switch (yy_input) { + case 9: yy_state = 48; break yy_forNext; + case 12: yy_state = 49; break yy_forNext; + default: break yy_forAction; + } + + case 15: + switch (yy_input) { + case 9: yy_state = 50; break yy_forNext; + case 17: yy_state = 51; break yy_forNext; + default: break yy_forAction; + } + + case 16: + switch (yy_input) { + case 12: yy_state = 52; break yy_forNext; + case 22: yy_state = 53; break yy_forNext; + default: break yy_forAction; + } + + case 17: + switch (yy_input) { + case 9: yy_state = 54; break yy_forNext; + case 29: yy_isFinal = true; yy_state = 55; break yy_forNext; + default: break yy_forAction; + } + + case 18: + switch (yy_input) { + case 9: yy_state = 56; break yy_forNext; + case 24: yy_state = 57; break yy_forNext; + default: break yy_forAction; + } + + case 19: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 19; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 58; break yy_forNext; + default: break yy_forAction; + } + + case 20: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 19; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 21; break yy_forNext; + default: break yy_forAction; + } + + case 21: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 59; break yy_forNext; + default: break yy_forAction; + } + + case 23: + switch (yy_input) { + case 6: + case 7: yy_isFinal = true; yy_state = 23; break yy_forNext; + case 8: yy_state = 60; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 61; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 22; break yy_forNext; + } + + case 24: + switch (yy_input) { + case 6: + case 7: yy_isFinal = true; yy_state = 23; break yy_forNext; + case 8: yy_state = 60; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 61; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 22; break yy_forNext; + } + + case 25: + switch (yy_input) { + case 31: yy_isFinal = true; yy_state = 26; break yy_forNext; + case 32: yy_isFinal = true; yy_state = 27; break yy_forNext; + case 6: + case 7: + case 8: yy_state = 60; break yy_forNext; + default: break yy_forAction; + } + + case 26: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 62; break yy_forNext; + default: break yy_forAction; + } + + case 27: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 63; break yy_forNext; + default: break yy_forAction; + } + + case 29: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 64; break yy_forNext; + default: break yy_forAction; + } + + case 30: + switch (yy_input) { + case 7: + case 8: + case 11: + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 32; break yy_forNext; + case 31: yy_isFinal = true; yy_state = 33; break yy_forNext; + case 12: + case 33: yy_state = 65; break yy_forNext; + default: break yy_forAction; + } + + case 31: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 32; break yy_forNext; + case 9: yy_state = 66; break yy_forNext; + default: break yy_forAction; + } + + case 32: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 67; break yy_forNext; + default: break yy_forAction; + } + + case 33: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 68; break yy_forNext; + default: break yy_forAction; + } + + case 34: + switch (yy_input) { + case 7: + case 8: + case 11: + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 32; break yy_forNext; + case 32: yy_isFinal = true; yy_state = 33; break yy_forNext; + case 33: yy_state = 65; break yy_forNext; + case 34: yy_state = 69; break yy_forNext; + default: break yy_forAction; + } + + case 35: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 9: yy_state = 70; break yy_forNext; + default: break yy_forAction; + } + + case 36: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 71; break yy_forNext; + default: break yy_forAction; + } + + case 37: + switch (yy_input) { + case 11: + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 36; break yy_forNext; + case 30: yy_isFinal = true; yy_state = 38; break yy_forNext; + case 31: + case 32: yy_isFinal = true; yy_state = 39; break yy_forNext; + case 33: yy_state = 65; break yy_forNext; + case 12: yy_state = 69; break yy_forNext; + default: break yy_forAction; + } + + case 38: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 72; break yy_forNext; + default: break yy_forAction; + } + + case 39: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 73; break yy_forNext; + default: break yy_forAction; + } + + case 42: + switch (yy_input) { + case 5: yy_isFinal = true; yy_noLookAhead = true; yy_state = 74; break yy_forNext; + default: break yy_forAction; + } + + case 43: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 43; break yy_forNext; + case 9: yy_state = 44; break yy_forNext; + case 11: yy_state = 45; break yy_forNext; + case 16: yy_state = 46; break yy_forNext; + default: break yy_forAction; + } + + case 44: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 43; break yy_forNext; + case 11: yy_state = 45; break yy_forNext; + case 16: yy_state = 46; break yy_forNext; + case 9: yy_state = 47; break yy_forNext; + default: break yy_forAction; + } + + case 45: + switch (yy_input) { + case 9: yy_state = 48; break yy_forNext; + case 12: yy_state = 49; break yy_forNext; + default: break yy_forAction; + } + + case 46: + switch (yy_input) { + case 9: yy_state = 50; break yy_forNext; + case 17: yy_state = 51; break yy_forNext; + default: break yy_forAction; + } + + case 47: + switch (yy_input) { + case 11: yy_state = 45; break yy_forNext; + case 16: yy_state = 46; break yy_forNext; + default: break yy_forAction; + } + + case 48: + switch (yy_input) { + case 12: yy_state = 49; break yy_forNext; + default: break yy_forAction; + } + + case 49: + switch (yy_input) { + case 9: yy_state = 75; break yy_forNext; + case 13: yy_state = 76; break yy_forNext; + default: break yy_forAction; + } + + case 50: + switch (yy_input) { + case 17: yy_state = 51; break yy_forNext; + default: break yy_forAction; + } + + case 51: + switch (yy_input) { + case 9: yy_state = 77; break yy_forNext; + case 18: yy_state = 78; break yy_forNext; + default: break yy_forAction; + } + + case 52: + switch (yy_input) { + case 9: yy_state = 54; break yy_forNext; + case 29: yy_isFinal = true; yy_state = 55; break yy_forNext; + default: break yy_forAction; + } + + case 53: + switch (yy_input) { + case 9: yy_state = 56; break yy_forNext; + case 24: yy_state = 57; break yy_forNext; + default: break yy_forAction; + } + + case 54: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 55; break yy_forNext; + default: break yy_forAction; + } + + case 55: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 79; break yy_forNext; + default: break yy_forAction; + } + + case 56: + switch (yy_input) { + case 24: yy_state = 57; break yy_forNext; + default: break yy_forAction; + } + + case 57: + switch (yy_input) { + case 9: yy_state = 80; break yy_forNext; + case 17: yy_state = 81; break yy_forNext; + default: break yy_forAction; + } + + case 58: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 19; break yy_forNext; + case 9: yy_state = 82; break yy_forNext; + default: break yy_forAction; + } + + case 60: + switch (yy_input) { + case 6: + case 7: yy_isFinal = true; yy_state = 23; break yy_forNext; + case 8: yy_state = 60; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 61; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 22; break yy_forNext; + } + + case 61: + switch (yy_input) { + case 6: + case 7: yy_isFinal = true; yy_state = 23; break yy_forNext; + case 8: yy_state = 60; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 83; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 22; break yy_forNext; + } + + case 65: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 32; break yy_forNext; + case 9: yy_state = 66; break yy_forNext; + default: break yy_forAction; + } + + case 66: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 32; break yy_forNext; + default: break yy_forAction; + } + + case 69: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 9: yy_state = 70; break yy_forNext; + default: break yy_forAction; + } + + case 70: + switch (yy_input) { + case 29: yy_isFinal = true; yy_state = 29; break yy_forNext; + default: break yy_forAction; + } + + case 75: + switch (yy_input) { + case 13: yy_state = 76; break yy_forNext; + default: break yy_forAction; + } + + case 76: + switch (yy_input) { + case 9: yy_state = 84; break yy_forNext; + case 14: yy_state = 85; break yy_forNext; + default: break yy_forAction; + } + + case 77: + switch (yy_input) { + case 18: yy_state = 78; break yy_forNext; + default: break yy_forAction; + } + + case 78: + switch (yy_input) { + case 9: yy_state = 86; break yy_forNext; + case 19: yy_state = 87; break yy_forNext; + default: break yy_forAction; + } + + case 80: + switch (yy_input) { + case 17: yy_state = 81; break yy_forNext; + default: break yy_forAction; + } + + case 81: + switch (yy_input) { + case 9: yy_state = 88; break yy_forNext; + case 25: yy_state = 89; break yy_forNext; + default: break yy_forAction; + } + + case 82: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 19; break yy_forNext; + default: break yy_forAction; + } + + case 83: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 60; break yy_forNext; + default: break yy_forAction; + } + + case 84: + switch (yy_input) { + case 14: yy_state = 85; break yy_forNext; + default: break yy_forAction; + } + + case 85: + switch (yy_input) { + case 9: yy_state = 90; break yy_forNext; + case 15: yy_state = 91; break yy_forNext; + default: break yy_forAction; + } + + case 86: + switch (yy_input) { + case 19: yy_state = 87; break yy_forNext; + default: break yy_forAction; + } + + case 87: + switch (yy_input) { + case 9: yy_state = 92; break yy_forNext; + case 20: yy_state = 93; break yy_forNext; + default: break yy_forAction; + } + + case 88: + switch (yy_input) { + case 25: yy_state = 89; break yy_forNext; + default: break yy_forAction; + } + + case 89: + switch (yy_input) { + case 9: yy_state = 94; break yy_forNext; + case 26: yy_state = 95; break yy_forNext; + default: break yy_forAction; + } + + case 90: + switch (yy_input) { + case 15: yy_state = 91; break yy_forNext; + default: break yy_forAction; + } + + case 91: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 96; break yy_forNext; + case 9: yy_state = 97; break yy_forNext; + default: break yy_forAction; + } + + case 92: + switch (yy_input) { + case 20: yy_state = 93; break yy_forNext; + default: break yy_forAction; + } + + case 93: + switch (yy_input) { + case 9: yy_state = 98; break yy_forNext; + case 21: yy_state = 99; break yy_forNext; + default: break yy_forAction; + } + + case 94: + switch (yy_input) { + case 26: yy_state = 95; break yy_forNext; + default: break yy_forAction; + } + + case 95: + switch (yy_input) { + case 9: yy_state = 100; break yy_forNext; + case 27: yy_state = 101; break yy_forNext; + default: break yy_forAction; + } + + case 96: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 96; break yy_forNext; + case 9: yy_isFinal = true; yy_state = 102; break yy_forNext; + default: break yy_forAction; + } + + case 97: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 96; break yy_forNext; + case 9: yy_state = 103; break yy_forNext; + default: break yy_forAction; + } + + case 98: + switch (yy_input) { + case 21: yy_state = 99; break yy_forNext; + default: break yy_forAction; + } + + case 99: + switch (yy_input) { + case 9: yy_state = 104; break yy_forNext; + case 22: yy_state = 105; break yy_forNext; + default: break yy_forAction; + } + + case 100: + switch (yy_input) { + case 27: yy_state = 101; break yy_forNext; + default: break yy_forAction; + } + + case 101: + switch (yy_input) { + case 9: yy_state = 106; break yy_forNext; + case 24: yy_state = 107; break yy_forNext; + default: break yy_forAction; + } + + case 102: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 96; break yy_forNext; + case 9: yy_state = 103; break yy_forNext; + default: break yy_forAction; + } + + case 103: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 96; break yy_forNext; + default: break yy_forAction; + } + + case 104: + switch (yy_input) { + case 22: yy_state = 105; break yy_forNext; + default: break yy_forAction; + } + + case 105: + switch (yy_input) { + case 9: yy_state = 108; break yy_forNext; + case 23: yy_isFinal = true; yy_state = 109; break yy_forNext; + default: break yy_forAction; + } + + case 106: + switch (yy_input) { + case 24: yy_state = 107; break yy_forNext; + default: break yy_forAction; + } + + case 107: + switch (yy_input) { + case 9: yy_state = 110; break yy_forNext; + case 28: yy_state = 111; break yy_forNext; + default: break yy_forAction; + } + + case 108: + switch (yy_input) { + case 23: yy_isFinal = true; yy_state = 109; break yy_forNext; + default: break yy_forAction; + } + + case 109: + switch (yy_input) { + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 112; break yy_forNext; + default: break yy_forAction; + } + + case 110: + switch (yy_input) { + case 28: yy_state = 111; break yy_forNext; + default: break yy_forAction; + } + + case 111: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 111; break yy_forNext; + case 9: yy_state = 113; break yy_forNext; + case 10: yy_isFinal = true; yy_state = 114; break yy_forNext; + default: break yy_forAction; + } + + case 113: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 111; break yy_forNext; + case 10: yy_isFinal = true; yy_state = 114; break yy_forNext; + case 9: yy_state = 115; break yy_forNext; + default: break yy_forAction; + } + + case 114: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 116; break yy_forNext; + case 9: yy_state = 117; break yy_forNext; + default: break yy_forAction; + } + + case 115: + switch (yy_input) { + case 6: + case 7: + case 8: yy_state = 111; break yy_forNext; + default: break yy_forAction; + } + + case 116: + switch (yy_input) { + case 9: yy_isFinal = true; yy_state = 114; break yy_forNext; + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 116; break yy_forNext; + default: break yy_forAction; + } + + case 117: + switch (yy_input) { + case 6: + case 7: + case 8: yy_isFinal = true; yy_state = 116; break yy_forNext; + default: break yy_forAction; + } + + default: + yy_ScanError(YY_ILLEGAL_STATE); + break; + } } + + if ( yy_isFinal ) { + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if ( yy_noLookAhead ) break yy_forAction; + } + + } + } + + + switch (yy_action) { + + case 29: + case 64: + { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 119: break; + case 22: + case 23: + case 25: + case 61: + case 83: + { yypushback(yylength()); yybegin(UnDelimitedString); string.setLength(0); } + case 120: break; + case 21: + case 59: + { yybegin(YYINITIAL); hasMore = false; return CSSHeadTokenizerConstants.RuleEnd; } + case 121: break; + case 39: + case 73: + { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue; } + case 122: break; + case 109: + case 112: + { if (yychar == 0 ) {yybegin(CHARSET_RULE); return CSSHeadTokenizerConstants.CHARSET_RULE;} } + case 123: break; + case 96: + case 102: + { if (yychar == 0 ) {yybegin(ST_XMLDecl); return XMLHeadTokenizerConstants.XMLDeclStart;} } + case 124: break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 20: + case 24: + case 30: + case 34: + case 37: + { if(yychar > MAX_TO_SCAN) {hasMore=false; return EncodingParserConstants.MAX_CHARS_REACHED;} } + case 125: break; + case 55: + case 79: + { yybegin(YYINITIAL); return XMLHeadTokenizerConstants.XMLDeclEnd; } + case 126: break; + case 114: + case 116: + { pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDelEncoding; } + case 127: break; + case 3: + case 19: + case 58: + { pushCurrentState(); yybegin(QuotedAttributeValue); } + case 128: break; + case 40: + { hasMore = false; return EncodingParserConstants.UTF16BE; } + case 129: break; + case 41: + { hasMore = false; return EncodingParserConstants.UTF16LE; } + case 130: break; + case 74: + { hasMore = false; return EncodingParserConstants.UTF83ByteBOM; } + case 131: break; + case 28: + case 31: + case 35: + { string.append( yytext() ); } + case 132: break; + case 27: + case 63: + { yybegin(SQ_STRING); string.setLength(0); } + case 133: break; + case 26: + case 62: + { yybegin(DQ_STRING); string.setLength(0); } + case 134: break; + case 32: + case 67: + { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 135: break; + case 33: + case 68: + { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + case 136: break; + case 36: + case 71: + { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + case 137: break; + case 38: + case 72: + { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + case 138: break; + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + { hasMore = false; return EncodingParserConstants.EOF; } + } + else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + /** + * Runs the scanner on input files. + * + * This main method is the debugging routine for the scanner. + * It prints each returned token to System.out until the end of + * file is reached, or an error occured. + * + * @param argv the command line, contains the filenames to run + * the scanner on. + */ + public static void main(String argv[]) { + for (int i = 0; i < argv.length; i++) { + CSSHeadTokenizer scanner = null; + try { + scanner = new CSSHeadTokenizer( new java.io.FileReader(argv[i]) ); + } + catch (java.io.FileNotFoundException e) { + System.out.println("File not found : \""+argv[i]+"\""); + System.exit(1); + } + catch (java.io.IOException e) { + System.out.println("Error opening file \""+argv[i]+"\""); + System.exit(1); + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Usage : java CSSHeadTokenizer <inputfile>"); + System.exit(1); + } + + try { + do { + System.out.println(scanner.primGetNextToken()); + } while (!scanner.yy_atEOF); + + } + catch (java.io.IOException e) { + System.out.println("An I/O error occured while scanning :"); + System.out.println(e); + System.exit(1); + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizerGenJava.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizerGenJava.cmd new file mode 100644 index 0000000..9ef06e9 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/CSSHeadTokenizerGenJava.cmd
@@ -0,0 +1,23 @@ +@echo off + +rem The following variables need to be set/specified for each "development machine" +set PATH=%PATH%;d:jdks\j2sdk1.4.1_02\bin +set WORKSPACE_LOCATION=D:\builds\Workspaces\PureHeadWorkspace +set JFLEX_LIB_LOCATION=D:\DevTimeSupport\JFlex\lib + +rem The following variables differ from project to project, but should be otherwise constant +set MAIN_NAME=CSSHeadTokenizer +set PROJECT_SRC=\org.eclipse.wst.common.encoding.contentspecific\src\ +set PACKAGE_DIR=com\ibm\encoding\resource\contentspecific\css\ + +rem Given the above "framework" and the command themselves, these variables should never need to be modified +set JAVA_FILE=%MAIN_NAME%.java +set JFLEX_RULES=%MAIN_NAME%.jflex +set SKELETON_FILE=%MAIN_NAME%.skeleton + +IF EXIST %JAVA_FILE% del %JAVA_FILE% +rem java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\sed-jflex.jar;. JFlex.Main %JFLEX_RULES% -skel %SKELETON_FILE% 1>jflexout.txt 2>jflexerr.txt +java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\sed-jflex.jar;. JFlex.Main %JFLEX_RULES% 1>jflexout.txt 2>jflexerr.txt +IF EXIST %JAVA_FILE% copy %JAVA_FILE% %WORKSPACE_LOCATION%%PROJECT_SRC%%PACKAGE_DIR%%JAVA_FILE% + +rem pause
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/jflexerr.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/jflexerr.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/jflexerr.txt
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/jflexout.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/jflexout.txt new file mode 100644 index 0000000..e0743ca --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/CSSHeadTokenizer/jflexout.txt
@@ -0,0 +1,6 @@ +Reading "CSSHeadTokenizer.jflex" +Constructing NFA : 458 states in NFA +Converting NFA to DFA : +........................................................................................ +102 states before minimization, 64 states in minimized DFA +Writing code to "CSSHeadTokenizer.java"
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.jFlex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.jFlex new file mode 100644 index 0000000..d35d074 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.jFlex
@@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 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 + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.html.core.internal.contenttype; +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.wst.xml.core.internal.contenttype.EncodingParserConstants; +import org.eclipse.wst.xml.core.internal.contenttype.XMLHeadTokenizerConstants; + + + +%% + +%{ + + + private boolean hasMore = true; + private boolean hasCharsetAttr = false; + private final static int MAX_TO_SCAN = 8000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + boolean foundContentTypeValue = false; + private boolean isXHTML; + + + + public HTMLHeadTokenizer() { + super(); + } + + public void reset (Reader in) { + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + // yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start + * of the matched text + */ + // yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning + * of a line + */ + yy_atBOL = false; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + fStateStack.clear(); + + hasMore = true; + hasCharsetAttr = false; + isXHTML = false; + + // its a little wasteful to "throw away" first char array generated + // by class init (via auto generated code), but we really do want + // a small buffer for our head parsers. + if (yy_buffer.length != MAX_TO_SCAN) { + yy_buffer = new char[MAX_TO_SCAN]; + } + + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + HeadParserToken result = null; + try { + context = primGetNextToken(); + } + catch (IllegalStateException e) { + hasMore = false; + result = createToken(HTMLHeadTokenizerConstants.UNDEFINED, yychar, yytext()); + while(yy_advance() != YYEOF) {} + return result; + } + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + public final boolean hasCharsetAttr() { + return hasCharsetAttr; + } + + private void popState() { + yybegin(fStateStack.pop()); + } + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + + public boolean isXHTML() { + return isXHTML; + } +%} + +%eof{ + hasMore=false; +%eof} + +%public +%class HTMLHeadTokenizer +%function primGetNextToken +%type String +%char +%unicode +%ignorecase +//%debug +%switch + + +UTF16BE = \xFE\xFF +UTF16LE = \xFF\xFE +UTF83ByteBOM = \xEF\xBB\xBF + +SpaceChar = [\x20\x09] + + + +// [3] S ::= (0x20 | 0x9 | 0xD | 0xA)+ +S = [\x20\x09\x0D\x0A] + +// BeginAttribeValue = {S}* \= {S}* + +LineTerminator = \r|\n + +Z = (\x00)? +S_UTF = {Z}{S}{Z} +BeginAttributeValueUTF = {S_UTF}* \= {S_UTF}* + +%state ST_XMLDecl +%state ST_META_TAG +%state QuotedAttributeValue +%state DQ_STRING +%state SQ_STRING +%state UnDelimitedString +%state UnDelimitedCharset + +%% + + +<YYINITIAL> +{ + {UTF16BE} {hasMore = false; return EncodingParserConstants.UTF16BE;} + {UTF16LE} {hasMore = false; return EncodingParserConstants.UTF16LE;} + {UTF83ByteBOM} {hasMore = false; return EncodingParserConstants.UTF83ByteBOM;} + + // force to be started on first line, but we do allow preceeding spaces + ^ {S_UTF}* ({Z}<{Z}\?{Z}x{Z}m{Z}l{Z}){S_UTF}+ {if (yychar == 0 ) {yybegin(ST_XMLDecl); return XMLHeadTokenizerConstants.XMLDeclStart;}} + ({Z}<{Z}M{Z}E{Z}T{Z}A{Z}) {yybegin(ST_META_TAG); return HTMLHeadTokenizerConstants.MetaTagStart;} + + // To determine if content is XHTML.Taken from JSPHeadTokenizer. + {Z}<{Z}\!{Z}D{Z}O{Z}C{Z}T{Z}Y{Z}P{Z}E{Z} {S_UTF}* {Z}h{Z}t{Z}m{Z}l{Z} {S_UTF}* {Z}P{Z}U{Z}B{Z}L{Z}I{Z}C{Z} .* {Z}\/{Z}\/{Z}D{Z}T{Z}D{Z}{S_UTF}{Z}X{Z}H{Z}T{Z}M{Z}L{Z} {isXHTML = true;} + {Z}<{Z}h{Z}t{Z}m{Z}l{Z} {S_UTF}* {Z}x{Z}m{Z}l{Z}n{Z}s{Z} {S_UTF}* {Z}\={Z} {S_UTF}* (({Z}\"{Z}) | ({Z}\'{Z})) {Z}h{Z}t{Z}t{Z}p{Z}:{Z}\/{Z}\/{Z}w{Z}w{Z}w{Z}\.{Z}w{Z}3{Z}\.{Z}o{Z}r{Z}g{Z}\/{Z}1{Z}9{Z}9{Z}9{Z}\/{Z}x{Z}h{Z}t{Z}m{Z}l{Z} {isXHTML = true;} + +} + +<ST_XMLDecl> +{ + //"version" {BeginAttribeValue} {pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDeclVersion;} + ({Z}e{Z}n{Z}c{Z}o{Z}d{Z}i{Z}n{Z}g{Z}) {BeginAttributeValueUTF} {pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDelEncoding;} + // note this "forced end" once end of XML Declaration found + ({Z}\?{Z}>{Z}) {yybegin(YYINITIAL); return XMLHeadTokenizerConstants.XMLDeclEnd;} +} + +<ST_META_TAG> +{ + +// "http-equiv" {S}* \= {S}* \"? "Content-Type" \"? {S}+ "content" {BeginAttribeValue} {pushCurrentState(); yybegin(QuotedAttributeValue); foundContentTypeValue=true; return HTMLHeadTokenizerConstants.MetaTagContentType;} + {Z}h{Z}t{Z}t{Z}p{Z}-{Z}e{Z}q{Z}u{Z}i{Z}v{Z} {S_UTF}* \= {S_UTF}* {Z}\"?{Z} ({Z}C{Z}o{Z}n{Z}t{Z}e{Z}n{Z}t{Z}-{Z}T{Z}y{Z}p{Z}e{Z}) \"?{Z} ({S_UTF})+ ({Z}c{Z}o{Z}n{Z}t{Z}e{Z}n{Z}t{Z}) {BeginAttributeValueUTF} {pushCurrentState(); yybegin(QuotedAttributeValue); foundContentTypeValue=true; return HTMLHeadTokenizerConstants.MetaTagContentType;} + {Z}c{Z}h{Z}a{Z}r{Z}s{Z}e{Z}t{Z} {BeginAttributeValueUTF} {pushCurrentState(); yybegin(QuotedAttributeValue); foundContentTypeValue=true; hasCharsetAttr=true; return HTMLHeadTokenizerConstants.MetaTagContentType;} + {Z}>{Z} { yybegin(YYINITIAL); if (foundContentTypeValue) hasMore = false; return HTMLHeadTokenizerConstants.MetaTagEnd;} + {Z}\/{Z}>{Z} { yybegin(YYINITIAL); if (foundContentTypeValue) hasMore = false; return HTMLHeadTokenizerConstants.MetaTagEnd;} +} + + +<QuotedAttributeValue> +{ + {Z}\"{Z} { yybegin(DQ_STRING); string.setLength(0); } + {Z}\'{Z} { yybegin(SQ_STRING); string.setLength(0); } + // in this state, anything other than a space character can start an undelimited string + {S_UTF}*. { yypushback(1); yybegin(UnDelimitedString); string.setLength(0);} + +} + + +<DQ_STRING> +{ + + {Z}\"{Z} { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + {Z}{LineTerminator}{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\?{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}<{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + + {Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\/{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + [^\x00] { string.append( yytext() ); } + + +} + +<SQ_STRING> +{ + + {Z}\'{Z} { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue;} + {Z}{LineTerminator}{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}%{Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}<{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\/{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + [^\x00] { string.append( yytext() ); } + + +} + +<UnDelimitedString> +{ + + + // note this initial special case for HTTP contenttype values + // Look ahead and see if there are spaces, but don't append the spaces as they may be double-byte + // Let the next state handle removal of the \x00 and properly append spaces + ";"/{S_UTF}* { pushCurrentState(); yybegin(UnDelimitedCharset); string.append( yytext() ); } + {S_UTF} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + {Z}{LineTerminator}{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\?{Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}<{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + // these are a bit special, since we started an undelimit string, but found a quote ... probably indicates a missing beginning quote + {Z}\'{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue;} + {Z}\"{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue;} + + {Z}>{Z} { yypushback(yylength());popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + {Z}\/{Z}>{Z} { yypushback(yylength()); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + [^\x00] { string.append( yytext() ); } + +} + +<UnDelimitedCharset> +{ + {S} { string.append( yytext() ); } + // For non \x00 characters, let the previous state handle it + [^\x00] {yypushback(1); popState(); } +} + +// The "match anything" rule should always be in effect except for when looking for end of string +// (That is, remember to update state list if/when new states added) +.|\n {if(yychar > MAX_TO_SCAN) {hasMore=false; return EncodingParserConstants.MAX_CHARS_REACHED;}} + +// this rule always in effect +<<EOF>> {hasMore = false; return EncodingParserConstants.EOF;}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.java b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.java new file mode 100644 index 0000000..87d6c8f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.java
@@ -0,0 +1,1206 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +/* The following code was generated by JFlex 1.2.2 on 1/27/04 6:41 PM */ + +/*nlsXXX*/ +package org.eclipse.wst.common.encoding.contentspecific.html; +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.wst.common.encoding.contentspecific.EncodingParserConstants; +import org.eclipse.wst.common.encoding.contentspecific.HeadParserToken; +import org.eclipse.wst.common.encoding.contentspecific.IntStack; +import org.eclipse.wst.common.encoding.contentspecific.xml.XMLHeadTokenizerConstants; + + + + +/** + * This class is a scanner generated by + * <a href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex</a> 1.2.2 + * on 1/27/04 6:41 PM from the specification file + * <tt>file:/D:/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizer.jflex</tt> + */ +public class HTMLHeadTokenizer { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int ST_META_TAG = 4; + final public static int YYINITIAL = 0; + final public static int UnDelimitedString = 12; + final public static int DQ_STRING = 8; + final public static int SQ_STRING = 10; + final public static int ST_XMLDecl = 2; + final public static int QuotedAttributeValue = 6; + + /** + * YY_LEXSTATE[l] is the state in the DFA for the lexical state l + * YY_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private final static int YY_LEXSTATE[] = { + 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 + }; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = + "\11\0\1\6\1\7\2\0\1\11\22\0\1\22\1\0\1\40\2\0"+ + "\1\44\1\0\1\43\5\0\1\34\1\0\1\42\13\0\1\45\1\12"+ + "\1\10\1\31\1\13\1\0\1\21\1\0\1\24\1\26\1\17\1\0"+ + "\1\30\1\32\1\27\2\0\1\16\1\15\1\23\1\25\1\33\1\35"+ + "\2\0\1\20\1\36\1\37\1\0\1\14\1\41\7\0\1\21\1\0"+ + "\1\24\1\26\1\17\1\0\1\30\1\32\1\27\2\0\1\16\1\15"+ + "\1\23\1\25\1\33\1\35\2\0\1\20\1\36\1\37\1\0\1\14"+ + "\1\41\101\0\1\4\3\0\1\5\17\0\1\3\16\0\1\1\20\0"+ + "\1\3\16\0\1\1\1\2\170\0\1\2\ufe87\0"; + + /** + * Translates characters to character classes + */ + final private static char [] yycmap = yy_unpack_cmap(yycmap_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", + "Internal error: unknown state", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + + + private boolean hasMore = true; + private final static int MAX_TO_SCAN = 8000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + boolean foundContentTypeValue = false; + + + + public HTMLHeadTokenizer() { + super(); + } + + public void reset (Reader in) { + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start + * of the matched text + */ + yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning + * of a line + */ + yy_atBOL = false; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + fStateStack.clear(); + + hasMore = true; + + // its a little wasteful to "throw away" first char array generated + // by class init (via auto generated code), but we really do want + // a small buffer for our head parsers. + if (yy_buffer.length != MAX_TO_SCAN) { + yy_buffer = new char[MAX_TO_SCAN]; + } + + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + context = primGetNextToken(); + HeadParserToken result = null; + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + + private void popState() { + yybegin(fStateStack.pop()); + } + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + + + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + public HTMLHeadTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public HTMLHeadTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] yy_unpack_cmap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 174) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning. + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + System.out.println(YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println(YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + + System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + private void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + hasMore=false; + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + + while (true) { + + yychar+= yylength(); + + yy_atBOL = yy_markedPos <= 0 || yy_buffer[yy_markedPos-1] == '\n'; + if (!yy_atBOL && yy_buffer[yy_markedPos-1] == '\r') { + yy_atBOL = yy_advance() != '\n'; + if (!yy_atEOF) yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + if (yy_atBOL) + yy_state = YY_LEXSTATE[yy_lexical_state+1]; + else + yy_state = YY_LEXSTATE[yy_lexical_state]; + + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + + yy_input = yycmap[yy_input]; + + boolean yy_isFinal = false; + boolean yy_noLookAhead = false; + + yy_forNext: { switch (yy_state) { + case 0: + switch (yy_input) { + case 1: yy_isFinal = true; yy_state = 9; break yy_forNext; + case 2: yy_isFinal = true; yy_state = 10; break yy_forNext; + case 3: yy_isFinal = true; yy_state = 11; break yy_forNext; + case 10: yy_isFinal = true; yy_state = 12; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 1: + switch (yy_input) { + case 1: yy_isFinal = true; yy_state = 9; break yy_forNext; + case 2: yy_isFinal = true; yy_state = 10; break yy_forNext; + case 3: yy_isFinal = true; yy_state = 11; break yy_forNext; + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_state = 13; break yy_forNext; + case 10: yy_isFinal = true; yy_state = 14; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 2: + switch (yy_input) { + case 11: yy_isFinal = true; yy_state = 15; break yy_forNext; + case 15: yy_isFinal = true; yy_state = 16; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 3: + switch (yy_input) { + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 17; break yy_forNext; + case 26: yy_isFinal = true; yy_state = 18; break yy_forNext; + case 34: yy_isFinal = true; yy_state = 19; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 8; break yy_forNext; + } + + case 4: + switch (yy_input) { + case 6: + case 9: + case 18: yy_isFinal = true; yy_state = 21; break yy_forNext; + case 7: yy_isFinal = true; yy_state = 22; break yy_forNext; + case 32: yy_isFinal = true; yy_noLookAhead = true; yy_state = 23; break yy_forNext; + case 35: yy_isFinal = true; yy_noLookAhead = true; yy_state = 24; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 5: + switch (yy_input) { + case 7: + case 9: + case 10: + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 26; break yy_forNext; + case 11: + case 34: yy_isFinal = true; yy_state = 27; break yy_forNext; + case 32: yy_isFinal = true; yy_noLookAhead = true; yy_state = 28; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 25; break yy_forNext; + } + + case 6: + switch (yy_input) { + case 7: + case 9: + case 10: + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 26; break yy_forNext; + case 34: yy_isFinal = true; yy_state = 27; break yy_forNext; + case 35: yy_isFinal = true; yy_noLookAhead = true; yy_state = 28; break yy_forNext; + case 36: yy_isFinal = true; yy_state = 29; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 25; break yy_forNext; + } + + case 7: + switch (yy_input) { + case 10: + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 26; break yy_forNext; + case 34: yy_isFinal = true; yy_state = 27; break yy_forNext; + case 11: yy_isFinal = true; yy_state = 29; break yy_forNext; + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_noLookAhead = true; yy_state = 30; break yy_forNext; + case 32: + case 35: yy_isFinal = true; yy_noLookAhead = true; yy_state = 31; break yy_forNext; + case 37: yy_isFinal = true; yy_state = 32; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 25; break yy_forNext; + } + + case 9: + switch (yy_input) { + case 2: yy_isFinal = true; yy_noLookAhead = true; yy_state = 33; break yy_forNext; + default: break yy_forAction; + } + + case 10: + switch (yy_input) { + case 1: yy_isFinal = true; yy_noLookAhead = true; yy_state = 34; break yy_forNext; + default: break yy_forAction; + } + + case 11: + switch (yy_input) { + case 4: yy_state = 35; break yy_forNext; + default: break yy_forAction; + } + + case 12: + switch (yy_input) { + case 13: yy_state = 36; break yy_forNext; + default: break yy_forAction; + } + + case 13: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 37; break yy_forNext; + case 10: yy_state = 38; break yy_forNext; + default: break yy_forAction; + } + + case 14: + switch (yy_input) { + case 13: yy_state = 36; break yy_forNext; + case 11: yy_state = 39; break yy_forNext; + default: break yy_forAction; + } + + case 15: + switch (yy_input) { + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 40; break yy_forNext; + default: break yy_forAction; + } + + case 16: + switch (yy_input) { + case 19: yy_state = 41; break yy_forNext; + default: break yy_forAction; + } + + case 18: + switch (yy_input) { + case 16: yy_state = 42; break yy_forNext; + default: break yy_forAction; + } + + case 19: + switch (yy_input) { + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 43; break yy_forNext; + default: break yy_forAction; + } + + case 21: + switch (yy_input) { + case 6: + case 9: + case 18: yy_isFinal = true; yy_state = 21; break yy_forNext; + case 7: yy_state = 44; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 22: + switch (yy_input) { + case 6: + case 9: + case 18: yy_isFinal = true; yy_state = 21; break yy_forNext; + case 7: yy_state = 44; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 27: + switch (yy_input) { + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 45; break yy_forNext; + default: break yy_forAction; + } + + case 29: + switch (yy_input) { + case 25: yy_isFinal = true; yy_noLookAhead = true; yy_state = 46; break yy_forNext; + default: break yy_forAction; + } + + case 32: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_state = 32; break yy_forNext; + default: break yy_forAction; + } + + case 35: + switch (yy_input) { + case 5: yy_isFinal = true; yy_noLookAhead = true; yy_state = 47; break yy_forNext; + default: break yy_forAction; + } + + case 36: + switch (yy_input) { + case 15: yy_state = 48; break yy_forNext; + default: break yy_forAction; + } + + case 37: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 37; break yy_forNext; + case 10: yy_state = 38; break yy_forNext; + default: break yy_forAction; + } + + case 38: + switch (yy_input) { + case 11: yy_state = 39; break yy_forNext; + default: break yy_forAction; + } + + case 39: + switch (yy_input) { + case 12: yy_state = 49; break yy_forNext; + default: break yy_forAction; + } + + case 41: + switch (yy_input) { + case 20: yy_state = 50; break yy_forNext; + default: break yy_forAction; + } + + case 42: + switch (yy_input) { + case 16: yy_state = 51; break yy_forNext; + default: break yy_forAction; + } + + case 44: + switch (yy_input) { + case 6: + case 9: + case 18: yy_isFinal = true; yy_state = 21; break yy_forNext; + case 7: yy_state = 44; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 48: + switch (yy_input) { + case 16: yy_state = 52; break yy_forNext; + default: break yy_forAction; + } + + case 49: + switch (yy_input) { + case 13: yy_state = 53; break yy_forNext; + default: break yy_forAction; + } + + case 50: + switch (yy_input) { + case 21: yy_state = 54; break yy_forNext; + default: break yy_forAction; + } + + case 51: + switch (yy_input) { + case 27: yy_state = 55; break yy_forNext; + default: break yy_forAction; + } + + case 52: + switch (yy_input) { + case 17: yy_state = 56; break yy_forNext; + default: break yy_forAction; + } + + case 53: + switch (yy_input) { + case 14: yy_state = 57; break yy_forNext; + default: break yy_forAction; + } + + case 54: + switch (yy_input) { + case 22: yy_state = 58; break yy_forNext; + default: break yy_forAction; + } + + case 55: + switch (yy_input) { + case 28: yy_state = 59; break yy_forNext; + default: break yy_forAction; + } + + case 56: + switch (yy_input) { + case 18: yy_isFinal = true; yy_noLookAhead = true; yy_state = 60; break yy_forNext; + default: break yy_forAction; + } + + case 57: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_state = 61; break yy_forNext; + default: break yy_forAction; + } + + case 58: + switch (yy_input) { + case 23: yy_state = 62; break yy_forNext; + default: break yy_forAction; + } + + case 59: + switch (yy_input) { + case 15: yy_state = 63; break yy_forNext; + default: break yy_forAction; + } + + case 61: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_state = 61; break yy_forNext; + default: break yy_forAction; + } + + case 62: + switch (yy_input) { + case 19: yy_state = 64; break yy_forNext; + default: break yy_forAction; + } + + case 63: + switch (yy_input) { + case 29: yy_state = 65; break yy_forNext; + default: break yy_forAction; + } + + case 64: + switch (yy_input) { + case 24: yy_state = 66; break yy_forNext; + default: break yy_forAction; + } + + case 65: + switch (yy_input) { + case 30: yy_state = 67; break yy_forNext; + default: break yy_forAction; + } + + case 66: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 66; break yy_forNext; + case 8: yy_isFinal = true; yy_state = 68; break yy_forNext; + default: break yy_forAction; + } + + case 67: + switch (yy_input) { + case 23: yy_state = 69; break yy_forNext; + default: break yy_forAction; + } + + case 68: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_state = 68; break yy_forNext; + default: break yy_forAction; + } + + case 69: + switch (yy_input) { + case 31: yy_state = 70; break yy_forNext; + default: break yy_forAction; + } + + case 70: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 70; break yy_forNext; + case 8: yy_state = 71; break yy_forNext; + default: break yy_forAction; + } + + case 71: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 71; break yy_forNext; + case 20: yy_state = 72; break yy_forNext; + case 32: yy_state = 73; break yy_forNext; + default: break yy_forAction; + } + + case 72: + switch (yy_input) { + case 21: yy_state = 74; break yy_forNext; + default: break yy_forAction; + } + + case 73: + switch (yy_input) { + case 20: yy_state = 72; break yy_forNext; + default: break yy_forAction; + } + + case 74: + switch (yy_input) { + case 19: yy_state = 75; break yy_forNext; + default: break yy_forAction; + } + + case 75: + switch (yy_input) { + case 16: yy_state = 76; break yy_forNext; + default: break yy_forAction; + } + + case 76: + switch (yy_input) { + case 15: yy_state = 77; break yy_forNext; + default: break yy_forAction; + } + + case 77: + switch (yy_input) { + case 19: yy_state = 78; break yy_forNext; + default: break yy_forAction; + } + + case 78: + switch (yy_input) { + case 16: yy_state = 79; break yy_forNext; + default: break yy_forAction; + } + + case 79: + switch (yy_input) { + case 28: yy_state = 80; break yy_forNext; + default: break yy_forAction; + } + + case 80: + switch (yy_input) { + case 16: yy_state = 81; break yy_forNext; + default: break yy_forAction; + } + + case 81: + switch (yy_input) { + case 33: yy_state = 82; break yy_forNext; + default: break yy_forAction; + } + + case 82: + switch (yy_input) { + case 27: yy_state = 83; break yy_forNext; + default: break yy_forAction; + } + + case 83: + switch (yy_input) { + case 15: yy_state = 84; break yy_forNext; + default: break yy_forAction; + } + + case 84: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 85; break yy_forNext; + case 32: yy_state = 86; break yy_forNext; + default: break yy_forAction; + } + + case 85: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 85; break yy_forNext; + case 20: yy_state = 87; break yy_forNext; + default: break yy_forAction; + } + + case 86: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 85; break yy_forNext; + default: break yy_forAction; + } + + case 87: + switch (yy_input) { + case 21: yy_state = 88; break yy_forNext; + default: break yy_forAction; + } + + case 88: + switch (yy_input) { + case 19: yy_state = 89; break yy_forNext; + default: break yy_forAction; + } + + case 89: + switch (yy_input) { + case 16: yy_state = 90; break yy_forNext; + default: break yy_forAction; + } + + case 90: + switch (yy_input) { + case 15: yy_state = 91; break yy_forNext; + default: break yy_forAction; + } + + case 91: + switch (yy_input) { + case 19: yy_state = 92; break yy_forNext; + default: break yy_forAction; + } + + case 92: + switch (yy_input) { + case 16: yy_state = 93; break yy_forNext; + default: break yy_forAction; + } + + case 93: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_state = 93; break yy_forNext; + case 8: yy_isFinal = true; yy_state = 94; break yy_forNext; + default: break yy_forAction; + } + + case 94: + switch (yy_input) { + case 6: + case 7: + case 9: + case 18: yy_isFinal = true; yy_state = 94; break yy_forNext; + default: break yy_forAction; + } + + default: + yy_ScanError(YY_ILLEGAL_STATE); + break; + } } + + if ( yy_isFinal ) { + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if ( yy_noLookAhead ) break yy_forAction; + } + + } + } + + + switch (yy_action) { + + case 26: + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 96: break; + case 20: + case 21: + { yypushback(1); yybegin(UnDelimitedString); string.setLength(0); } + case 97: break; + case 17: + { yybegin(YYINITIAL); if (foundContentTypeValue) hasMore = false; return HTMLHeadTokenizerConstants.MetaTagEnd; } + case 98: break; + case 31: + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue; } + case 99: break; + case 43: + { yybegin(YYINITIAL); if (foundContentTypeValue) hasMore = false; return HTMLHeadTokenizerConstants.MetaTagEnd; } + case 100: break; + case 45: + { yypushback(2); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 101: break; + case 46: + { yypushback(2);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 102: break; + case 61: + { if (yychar == 0 ) {yybegin(ST_XMLDecl); return XMLHeadTokenizerConstants.XMLDeclStart;} } + case 103: break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 18: + case 19: + case 22: + { if (yychar > MAX_TO_SCAN) {hasMore=false; return EncodingParserConstants.MAX_CHARS_REACHED;} } + case 104: break; + case 60: + { yybegin(ST_META_TAG); return HTMLHeadTokenizerConstants.MetaTagStart; } + case 105: break; + case 40: + { yybegin(YYINITIAL); return XMLHeadTokenizerConstants.XMLDeclEnd; } + case 106: break; + case 94: + { pushCurrentState(); yybegin(QuotedAttributeValue); foundContentTypeValue=true; return HTMLHeadTokenizerConstants.MetaTagContentType; } + case 107: break; + case 68: + { pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDelEncoding; } + case 108: break; + case 33: + { hasMore = false; return EncodingParserConstants.UTF16BE; } + case 109: break; + case 34: + { hasMore = false; return EncodingParserConstants.UTF16LE; } + case 110: break; + case 47: + { hasMore = false; return EncodingParserConstants.UTF83ByteBOM; } + case 111: break; + case 28: + { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + case 112: break; + case 25: + case 27: + case 29: + case 32: + { string.append( yytext() ); } + case 113: break; + case 24: + { yybegin(SQ_STRING); string.setLength(0); } + case 114: break; + case 23: + { yybegin(DQ_STRING); string.setLength(0); } + case 115: break; + case 30: + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + case 116: break; + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + { hasMore = false; return EncodingParserConstants.EOF; } + } + else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + /** + * Runs the scanner on input files. + * + * This main method is the debugging routine for the scanner. + * It prints each returned token to System.out until the end of + * file is reached, or an error occured. + * + * @param argv the command line, contains the filenames to run + * the scanner on. + */ + public static void main(String argv[]) { + for (int i = 0; i < argv.length; i++) { + HTMLHeadTokenizer scanner = null; + try { + scanner = new HTMLHeadTokenizer( new java.io.FileReader(argv[i]) ); + } + catch (java.io.FileNotFoundException e) { + System.out.println("File not found : \""+argv[i]+"\""); + System.exit(1); + } + catch (java.io.IOException e) { + System.out.println("Error opening file \""+argv[i]+"\""); + System.exit(1); + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Usage : java HTMLHeadTokenizer <inputfile>"); + System.exit(1); + } + + try { + do { + System.out.println(scanner.primGetNextToken()); + } while (!scanner.yy_atEOF); + + } + catch (java.io.IOException e) { + System.out.println("An I/O error occured while scanning :"); + System.out.println(e); + System.exit(1); + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizerGenJava.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizerGenJava.cmd new file mode 100644 index 0000000..96f2325 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/HTMLHeadTokenizerGenJava.cmd
@@ -0,0 +1,23 @@ +@echo off + +rem The following variables need to be set/specified for each "development machine" +set PATH=%PATH%;d:jdks\j2sdk1.4.1_02\bin +set WORKSPACE_LOCATION=D:\builds\Workspaces\PureHeadWorkspace +set JFLEX_LIB_LOCATION=D:\DevTimeSupport\JFlex\lib + +rem The following variables differ from project to project, but should be otherwise constant +set MAIN_NAME=HTMLHeadTokenizer +set PROJECT_SRC=\org.eclipse.wst.common.encoding.contentspecific\src\ +set PACKAGE_DIR=com\ibm\encoding\resource\contentspecific\html\ + +rem Given the above "framework" and the command themselves, these variables should never need to be modified +set JAVA_FILE=%MAIN_NAME%.java +set JFLEX_RULES=%MAIN_NAME%.jflex +set SKELETON_FILE=%MAIN_NAME%.skeleton + +IF EXIST %JAVA_FILE% del %JAVA_FILE% +rem java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\sed-jflex.jar;. JFlex.Main %JFLEX_RULES% -skel %SKELETON_FILE% 1>jflexout.txt 2>jflexerr.txt +java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\sed-jflex.jar;. JFlex.Main %JFLEX_RULES% 1>jflexout.txt 2>jflexerr.txt +IF EXIST %JAVA_FILE% copy %JAVA_FILE% %WORKSPACE_LOCATION%%PROJECT_SRC%%PACKAGE_DIR%%JAVA_FILE% + +rem pause
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/jflexerr.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/jflexerr.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/jflexerr.txt
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/jflexout.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/jflexout.txt new file mode 100644 index 0000000..1469e9c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/jflexout.txt
@@ -0,0 +1,8 @@ +Reading "HTMLHeadTokenizer.jflex" + +Warning : Macro "SpaceChar" has been declared but never used. +Constructing NFA : 660 states in NFA +Converting NFA to DFA : +......................................................................................................................... +135 states before minimization, 95 states in minimized DFA +Writing code to "HTMLHeadTokenizer.java"
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/skeleton b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/skeleton new file mode 100644 index 0000000..4a9b3f7 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/skeleton
@@ -0,0 +1,268 @@ + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ +--- lexical states, charmap + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", + "Internal error: unknown state", + "Error: could not match input", + "Error: pushback value was too large" + }; + +--- isFinal list + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + //private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + //private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + +--- user class code + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ +--- constructor declaration + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning. + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + IllegalStateException ise = new IllegalStateException("Instance: " + System.identityHashCode(this) + " offset:" + yychar + " state:" + yystate()); + System.out.println(YY_ERROR_MSG[errorCode] + "\n" + ise); + throw ise; + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println(YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + private void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + +--- yy_doEof + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ +--- yylex declaration + int yy_input; + int yy_action; + +--- local declarations + + while (true) { + +--- start admin (line, char, col count) + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + +--- start admin (lexstate etc) + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + +--- line, col, char count, next transition, isFinal action + yy_action = yy_state; + yy_markedPos = yy_currentPos; +--- line count update + } + + } + } + +--- char count update + + switch (yy_action) { + +--- actions + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; +--- eofvalue + } + else { +--- no match + } + } + } + } + +--- main + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/skeleton.readme b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/skeleton.readme new file mode 100644 index 0000000..eb89700 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/HTMLHeadTokenizer/skeleton.readme
@@ -0,0 +1,6 @@ +The skeleton.sed file contains the modified JFlex 1.2.2 skeleton file with +changes for use with the tokenizers within the org.eclipse.wst.sse.core.xml and +org.eclipse.wst.sse.core.jsp plugins. + +The skeleton file's method definitions are copied into the generated output +directly.
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10Names.jFlex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10Names.jFlex new file mode 100644 index 0000000..d307389 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10Names.jFlex
@@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2004,2008 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 + * kb.huang - Bug 214416 Dot char is not escaped in XML10Names.jFlex + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.xml.core.internal.parser; + + + +%% + +%table +%public +%final +%class XML10Names +%function isValidXML10Name +%type boolean +%unicode +%ignorecase +%buffer 2048 +%apiprivate + +BaseChar = [\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3] + +Ideographic = [\u4E00-\u9FA5\u3007\u3021-\u3029] + +CombiningChar = [\u0300-\u0345\u0360-\u0361\u0483-\u0486\u0591-\u05A1\u05A3-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4\u064B-\u0652\u0670\u06D6-\u06DC\u06DD-\u06DF\u06E0-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0901-\u0903\u093C\u093E-\u094C\u094D\u0951-\u0954\u0962-\u0963\u0981-\u0983\u09BC\u09BE\u09BF\u09C0-\u09C4\u09C7-\u09C8\u09CB-\u09CD\u09D7\u09E2-\u09E3\u0A02\u0A3C\u0A3E\u0A3F\u0A40-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A70-\u0A71\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0B01-\u0B03\u0B3C\u0B3E-\u0B43\u0B47-\u0B48\u0B4B-\u0B4D\u0B56-\u0B57\u0B82-\u0B83\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C82-\u0C83\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5-\u0CD6\u0D02-\u0D03\u0D3E-\u0D43\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86-\u0F8B\u0F90-\u0F95\u0F97\u0F99-\u0FAD\u0FB1-\u0FB7\u0FB9\u20D0-\u20DC\u20E1\u302A-\u302F\u3099\u309A] + +Digit = [\u0030-\u0039\u0660-\u0669\u06F0-\u06F9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE7-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29] + +Extender = [\u00B7\u02D0\u02D1\u0387\u0640\u0E46\u0EC6\u3005\u3031-\u3035\u309D-\u309E\u30FC-\u30FE] + +Letter = ({BaseChar} | {Ideographic}) + +NameChar = ({Letter} | {Digit} | "." | "-" | "_" | ":" | {CombiningChar} | {Extender}) + +Name = ({Letter} | "_" | ":" ) ({NameChar})* + + + +%{ + boolean result; + /** + * Creates a new scanner + */ + public XML10Names() { + this.zzReader = null; + } + + public boolean isValidXML10Name(String stringToCheck) { + boolean result = false; + yyreset(new java.io.StringReader(stringToCheck)); + try { + result = isValidXML10Name(); + } + catch (java.io.IOException e) { + // should be impossible with strings, but if occurs, just means + // "not" + result = false; + } + return result; + } + +%} + + + + +%% + +<YYINITIAL> +{ + + // name must be whole line (input), not partial + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=214416 + {Name} {return true; /* exact name */} + {Name}.+ {return false; /* more than name */} + + // match anything (else) should normally come last + . {return false; /* matched wild */} + +} + + +// this rule always in effect +<<EOF>> +{ + {return false; /* hit end with no match */} +} + +
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10Names.java b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10Names.java new file mode 100644 index 0000000..fe91212 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10Names.java
@@ -0,0 +1,595 @@ +/* The following code was generated by JFlex 1.4 on 2/25/08 10:25 AM */ + +/******************************************************************************* + * Copyright (c) 2004,2008 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 + * kb.huang - Bug 214416 Dot char is not escaped in XML10Names.jFlex + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.xml.core.internal.parser; + + + + +/** + * This class is a scanner generated by + * <a href="http://www.jflex.de/">JFlex</a> 1.4 + * on 2/25/08 10:25 AM from the specification file + * <tt>XML10Names.jflex</tt> + */ +public final class XML10Names { + + /** This character denotes the end of file */ + private static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 2048; + + /** lexical states */ + private static final int YYINITIAL = 0; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = + "\12\0\1\3\42\0\1\2\1\2\1\0\12\2\1\1\6\0\32\1"+ + "\4\0\1\1\1\0\32\1\74\0\1\2\10\0\27\1\1\0\37\1"+ + "\1\0\72\1\2\0\13\1\2\0\10\1\1\0\65\1\1\0\104\1"+ + "\11\0\44\1\3\0\2\1\4\0\36\1\70\0\131\1\22\0\7\1"+ + "\16\0\2\2\56\0\106\2\32\0\2\2\44\0\1\1\1\2\3\1"+ + "\1\0\1\1\1\0\24\1\1\0\54\1\1\0\7\1\3\0\1\1"+ + "\1\0\1\1\1\0\1\1\1\0\1\1\1\0\22\1\15\0\14\1"+ + "\1\0\102\1\1\0\14\1\1\0\44\1\1\0\4\2\11\0\65\1"+ + "\2\0\2\1\2\0\2\1\3\0\34\1\2\0\10\1\2\0\2\1"+ + "\67\0\46\1\2\0\1\1\7\0\46\1\12\0\21\2\1\0\27\2"+ + "\1\0\3\2\1\0\1\2\1\0\2\2\1\0\1\2\13\0\33\1"+ + "\5\0\3\1\56\0\32\1\5\0\1\2\12\1\10\2\15\0\12\2"+ + "\6\0\1\2\107\1\2\0\5\1\1\0\17\1\1\0\4\1\1\0"+ + "\1\1\17\2\2\1\2\2\1\0\4\2\2\0\12\2\u0207\0\3\2"+ + "\1\0\65\1\2\0\1\2\1\1\20\2\3\0\4\2\3\0\12\1"+ + "\2\2\2\0\12\2\21\0\3\2\1\0\10\1\2\0\2\1\2\0"+ + "\26\1\1\0\7\1\1\0\1\1\3\0\4\1\2\0\1\2\1\0"+ + "\7\2\2\0\2\2\2\0\3\2\11\0\1\2\4\0\2\1\1\0"+ + "\3\1\2\2\2\0\12\2\2\1\20\0\1\2\2\0\6\1\4\0"+ + "\2\1\2\0\26\1\1\0\7\1\1\0\2\1\1\0\2\1\1\0"+ + "\2\1\2\0\1\2\1\0\5\2\4\0\2\2\2\0\3\2\13\0"+ + "\4\1\1\0\1\1\7\0\12\2\2\2\3\1\14\0\3\2\1\0"+ + "\7\1\1\0\1\1\1\0\3\1\1\0\26\1\1\0\7\1\1\0"+ + "\2\1\1\0\5\1\2\0\1\2\1\1\10\2\1\0\3\2\1\0"+ + "\3\2\22\0\1\1\5\0\12\2\21\0\3\2\1\0\10\1\2\0"+ + "\2\1\2\0\26\1\1\0\7\1\1\0\2\1\2\0\4\1\2\0"+ + "\1\2\1\1\6\2\3\0\2\2\2\0\3\2\10\0\2\2\4\0"+ + "\2\1\1\0\3\1\4\0\12\2\22\0\2\2\1\0\6\1\3\0"+ + "\3\1\1\0\4\1\3\0\2\1\1\0\1\1\1\0\2\1\3\0"+ + "\2\1\3\0\3\1\3\0\10\1\1\0\3\1\4\0\5\2\3\0"+ + "\3\2\1\0\4\2\11\0\1\2\17\0\11\2\21\0\3\2\1\0"+ + "\10\1\1\0\3\1\1\0\27\1\1\0\12\1\1\0\5\1\4\0"+ + "\7\2\1\0\3\2\1\0\4\2\7\0\2\2\11\0\2\1\4\0"+ + "\12\2\22\0\2\2\1\0\10\1\1\0\3\1\1\0\27\1\1\0"+ + "\12\1\1\0\5\1\4\0\7\2\1\0\3\2\1\0\4\2\7\0"+ + "\2\2\7\0\1\1\1\0\2\1\4\0\12\2\22\0\2\2\1\0"+ + "\10\1\1\0\3\1\1\0\27\1\1\0\20\1\4\0\6\2\2\0"+ + "\3\2\1\0\4\2\11\0\1\2\10\0\2\1\4\0\12\2\221\0"+ + "\56\1\1\0\1\1\1\2\2\1\7\2\5\0\6\1\1\2\10\2"+ + "\1\0\12\2\47\0\2\1\1\0\1\1\2\0\2\1\1\0\1\1"+ + "\2\0\1\1\6\0\4\1\1\0\7\1\1\0\3\1\1\0\1\1"+ + "\1\0\1\1\2\0\2\1\1\0\2\1\1\0\1\1\1\2\2\1"+ + "\6\2\1\0\2\2\1\1\2\0\5\1\1\0\1\2\1\0\6\2"+ + "\2\0\12\2\76\0\2\2\6\0\12\2\13\0\1\2\1\0\1\2"+ + "\1\0\1\2\4\0\2\2\10\1\1\0\41\1\7\0\24\2\1\0"+ + "\6\2\4\0\6\2\1\0\1\2\1\0\25\2\3\0\7\2\1\0"+ + "\1\2\346\0\46\1\12\0\47\1\11\0\1\1\1\0\2\1\1\0"+ + "\3\1\1\0\1\1\1\0\2\1\1\0\5\1\51\0\1\1\1\0"+ + "\1\1\1\0\1\1\13\0\1\1\1\0\1\1\1\0\1\1\3\0"+ + "\2\1\3\0\1\1\5\0\3\1\1\0\1\1\1\0\1\1\1\0"+ + "\1\1\1\0\1\1\3\0\2\1\3\0\2\1\1\0\1\1\50\0"+ + "\1\1\11\0\1\1\2\0\1\1\2\0\2\1\7\0\2\1\1\0"+ + "\1\1\1\0\7\1\50\0\1\1\4\0\1\1\10\0\1\1\u0c06\0"+ + "\234\1\4\0\132\1\6\0\26\1\2\0\6\1\2\0\46\1\2\0"+ + "\6\1\2\0\10\1\1\0\1\1\1\0\1\1\1\0\1\1\1\0"+ + "\37\1\2\0\65\1\1\0\7\1\1\0\1\1\3\0\3\1\1\0"+ + "\7\1\3\0\4\1\2\0\6\1\4\0\15\1\5\0\3\1\1\0"+ + "\7\1\323\0\15\2\4\0\1\2\104\0\1\1\3\0\2\1\2\0"+ + "\1\1\121\0\3\1\u0e82\0\1\2\1\0\1\1\31\0\11\1\6\2"+ + "\1\0\5\2\13\0\124\1\4\0\2\2\2\0\2\2\2\0\132\1"+ + "\1\0\3\2\6\0\50\1\u1cd3\0\u51a6\1\u0c5a\0\u2ba4\1\u285c\0"; + + /** + * Translates characters to character classes + */ + private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int [] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = + "\1\0\1\1\1\2\1\3"; + + private static int [] zzUnpackAction() { + int [] result = new int[4]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = + "\0\0\0\4\0\10\0\14"; + + private static int [] zzUnpackRowMap() { + int [] result = new int[4]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int ZZ_TRANS [] = { + 1, 2, 1, -1, -1, -1, -1, -1, 3, 2, + 2, -1, 3, 3, 3, -1 + }; + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = { + "Unkown internal scanner error", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code> + */ + private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = + "\1\0\1\11\2\1"; + + private static int [] zzUnpackAttribute() { + int [] result = new int[4]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int [] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do result[j++] = value; while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the textposition at the last state to be included in yytext */ + private int zzPushbackPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * zzAtBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean zzAtBOL = true; + + /** zzAtEOF == true <=> the scanner is at the EOF */ + private boolean zzAtEOF; + + /* user code: */ + boolean result; + /** + * Creates a new scanner + */ + public XML10Names() { + this.zzReader = null; + } + + public boolean isValidXML10Name(String stringToCheck) { + boolean result = false; + yyreset(new java.io.StringReader(stringToCheck)); + try { + result = isValidXML10Name(); + } + catch (java.io.IOException e) { + // should be impossible with strings, but if occurs, just means + // "not" + result = false; + } + return result; + } + + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + public XML10Names(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public XML10Names(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] zzUnpackCMap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 1218) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return <code>false</code>, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, + zzBuffer, 0, + zzEndRead-zzStartRead); + + /* translate stored positions */ + zzEndRead-= zzStartRead; + zzCurrentPos-= zzStartRead; + zzMarkedPos-= zzStartRead; + zzPushbackPos-= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[zzCurrentPos*2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, + zzBuffer.length-zzEndRead); + + if (numRead < 0) { + return true; + } + else { + zzEndRead+= numRead; + return false; + } + } + + + /** + * Closes the input stream. + */ + private final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. + * Does not close the old reader. + * + * All internal variables are reset, the old input stream + * <b>cannot</b> be reused (internal buffer is discarded and lost). + * Lexical state is set to <tt>ZZ_INITIAL</tt>. + * + * @param reader the new input stream + */ + private final void yyreset(java.io.Reader reader) { + zzReader = reader; + zzAtBOL = true; + zzAtEOF = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = zzPushbackPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + private final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + private final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + private final String yytext() { + return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + } + + + /** + * Returns the character at position <tt>pos</tt> from the + * matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos the position of the character to fetch. + * A value from 0 to yylength()-1. + * + * @return the character at position pos + */ + private final char yycharat(int pos) { + return zzBuffer[zzStartRead+pos]; + } + + + /** + * Returns the length of the matched text region. + */ + private final int yylength() { + return zzMarkedPos-zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method + * will only be called with things that "Can't Possibly Happen". + * If this method is called, something is seriously wrong + * (e.g. a JFlex bug producing a faulty scanner etc.). + * + * Usual syntax/scanner level error handling should be done + * in error fallback rules. + * + * @param errorCode the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } + catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + private void yypushback(int number) { + if ( number > yylength() ) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException if any I/O-Error occurs + */ + private boolean isValidXML10Name() throws java.io.IOException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char [] zzBufferL = zzBuffer; + char [] zzCMapL = ZZ_CMAP; + + int [] zzTransL = ZZ_TRANS; + int [] zzRowMapL = ZZ_ROWMAP; + int [] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = zzLexicalState; + + + zzForAction: { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } + else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } + else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + if (zzNext == -1) break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ( (zzAttributes & 1) == 1 ) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ( (zzAttributes & 8) == 8 ) break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1: + { return false; /* matched wild */ + } + case 4: break; + case 2: + { return true; /* exact name */ + } + case 5: break; + case 3: + { return false; /* more than name */ + } + case 6: break; + default: + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + { {return false; /* hit end with no match */} } + } + else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10NamesGenJavaJFlex14.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10NamesGenJavaJFlex14.cmd new file mode 100644 index 0000000..7c816d0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/XML10NamesGenJavaJFlex14.cmd
@@ -0,0 +1,25 @@ +@echo on + +rem The following variables need to be set/specified for each "development machine" +set PATH=%PATH%;D:\JDKs\ibm-java2-sdk-50-win-i386\bin +set WORKSPACE_LOCATION=D:\builds\Workspaces\newWTPpurehead +set JFLEX_LIB_LOCATION=D:\DevTimeSupport\JFlex-1.4\lib + +rem The following variables differ from project to project, but should be otherwise constant +set MAIN_NAME=XML10Names + +set PROJECT_SRC=\org.eclipse.wst.xml.core\src\ +set PACKAGE_DIR=org\eclipse\wst\xml\core\internal\parser\ + + +rem Given the above "framework" and the command themselves, these variables should never need to be modified +set JAVA_FILE=%MAIN_NAME%.java +set JFLEX_RULES=%MAIN_NAME%.jflex +set SKELETON_FILE=%MAIN_NAME%.skeleton + +IF EXIST %JAVA_FILE% del %JAVA_FILE% +rem java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\Jflex.jar;. JFlex.Main %JFLEX_RULES% -skel %SKELETON_FILE% 1>jflexout.txt 2>jflexerr.txt +java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\Jflex.jar;. JFlex.Main %JFLEX_RULES% 1>jflexout.txt 2>jflexerr.txt +IF EXIST %JAVA_FILE% copy %JAVA_FILE% %WORKSPACE_LOCATION%%PROJECT_SRC%%PACKAGE_DIR%%JAVA_FILE% + +pause
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/jflexerr.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/jflexerr.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/jflexerr.txt
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/jflexout.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/jflexout.txt new file mode 100644 index 0000000..c336f12 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XML10Names/jflexout.txt
@@ -0,0 +1,6 @@ +Reading "XML10Names.jflex" +Constructing NFA : 84 states in NFA +Converting NFA to DFA : +............ +14 states before minimization, 4 states in minimized DFA +Writing code to "XML10Names.java"
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.jFlex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.jFlex new file mode 100644 index 0000000..d6fe899 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.jFlex
@@ -0,0 +1,260 @@ +/******************************************************************************* + * Copyright (c) 2004 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 + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.common.encoding.contentspecific.xml; +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.wst.common.encoding.contentspecific.EncodingParserConstants; +import org.eclipse.wst.common.encoding.contentspecific.HeadParserToken; +import org.eclipse.wst.common.encoding.contentspecific.IntStack; +import org.eclipse.wst.common.encoding.contentspecific.xml.XMLHeadTokenizerConstants; + + + + +%% + +%{ + + + private boolean hasMore = true; + private final static int MAX_TO_SCAN = 8000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + + + public XMLHeadTokenizer() { + super(); + } + + public void reset (Reader in) { + /* the input device */ + zzReader = in; + + /* the current state of the DFA */ + zzState = 0; + + /* the current lexical state */ + zzLexicalState = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(zzBuffer, (char)0); + + /* the textposition at the last accepting state */ + zzMarkedPos = 0; + + /* the textposition at the last state to be included in yytext */ + zzPushbackPos = 0; + + /* the current text position in the buffer */ + zzCurrentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + zzStartRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + zzEndRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start + * of the matched text + */ + yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning + * of a line + */ + zzAtBOL = true; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + zzAtEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + zzEOFDone = false; + + + fStateStack.clear(); + + hasMore = true; + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + context = primGetNextToken(); + HeadParserToken result = null; + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + + private void popState() { + yybegin(fStateStack.pop()); + } + + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + +%} + +%eof{ + hasMore=false; +%eof} + +%public +%class XMLHeadTokenizer +%function primGetNextToken +%type String +%char +%unicode +%ignorecase +//%debug +%switch +%buffer 8192 + +UTF16BE = \xFE\xFF +UTF16LE = \xFF\xFE +UTF83ByteBOM = \xEF\xBB\xBF + +SpaceChar = [\x20\x09] + +// [3] S ::= (0x20 | 0x9 | 0xD | 0xA)+ +S = [\x20\x09\x0D\x0A] + +BeginAttribeValue = {S}* \= {S}* + +LineTerminator = \r|\n + + +%state ST_XMLDecl +%state QuotedAttributeValue +%state DQ_STRING +%state SQ_STRING +%state UnDelimitedString + +%% + + +<YYINITIAL> +{ + // force to start at beginning of line (^) and at beginning of file (yychar == 0) + ^{UTF16BE} {if (yychar == 0 ) {hasMore = false; return EncodingParserConstants.UTF16BE;}} + ^{UTF16LE} {if (yychar == 0 ) {hasMore = false; return EncodingParserConstants.UTF16LE;}} + ^{UTF83ByteBOM} {if (yychar == 0 ) {hasMore = false; return EncodingParserConstants.UTF83ByteBOM;}} + + // force to be started on first line, but we do allow preceeding spaces + ^ {S}* "<\?xml" {S}+ {if (yychar == 0 ) {yybegin(ST_XMLDecl); return XMLHeadTokenizerConstants.XMLDeclStart;}} + +} + +<ST_XMLDecl> +{ + "version" {BeginAttribeValue} {pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDeclVersion;} + "encoding" {BeginAttribeValue} {pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDelEncoding;} + // note the "forced end" (via 'hasMore=false') once the end of XML Declaration found + // This is since non-ascii chars may follow and may cause IOExceptions which would not occur once stream is + // read with incorrect encoding (such as if platform encoding is in effect until true encoding detected). + "\?>" {yybegin(YYINITIAL); hasMore = false; return XMLHeadTokenizerConstants.XMLDeclEnd;} +} + + + +<QuotedAttributeValue> +{ + \" { yybegin(DQ_STRING); string.setLength(0); } + \' { yybegin(SQ_STRING); string.setLength(0); } + // in this state, anything other than a space character can start an undelimited string + {S}*. { yypushback(1); yybegin(UnDelimitedString); string.setLength(0);} + +} + + +<DQ_STRING> +{ + + \" { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + {LineTerminator} { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + "\?>" { yypushback(2); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + '<' { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + . { string.append( yytext() ); } + + +} + +<SQ_STRING> +{ + + \' { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue;} + {LineTerminator} { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + "%>" { yypushback(2);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + '<' { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + . { string.append( yytext() ); } + + +} + +<UnDelimitedString> +{ + + {S} { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + {LineTerminator} { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + "\?>" { yypushback(2);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + '<' + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue;} + // these are a bit special, since we started an undelimit string, but found a quote ... probably indicates a missing beginning quote + \' { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue;} + + \" { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue;} + + . { string.append( yytext() ); } + +} + +// The "match anything" rule should always be in effect except for when looking for end of string +// (That is, remember to update state list if/when new states added) +<YYINITIAL, ST_XMLDecl, QuotedAttributeValue> +{ +// this is the fallback (match "anything") rule (for this scanner, input is ignored, and position advanced, if not recognized) +.|\n {if (yychar > MAX_TO_SCAN) {hasMore=false; return EncodingParserConstants.MAX_CHARS_REACHED;}} +} + +// this rule always in effect +<<EOF>> {hasMore = false; return EncodingParserConstants.EOF;} + + \ No newline at end of file
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.java b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.java new file mode 100644 index 0000000..b353067 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.java
@@ -0,0 +1,905 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +/* The following code was generated by JFlex 1.2.2 on 4/6/04 11:13 PM */ + +/*nlsXXX*/ +package org.eclipse.wst.common.encoding.contentspecific.xml; +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.wst.common.encoding.contentspecific.EncodingParserConstants; +import org.eclipse.wst.common.encoding.contentspecific.HeadParserToken; +import org.eclipse.wst.common.encoding.contentspecific.IntStack; +import org.eclipse.wst.common.encoding.contentspecific.xml.XMLHeadTokenizerConstants; + + + + + +/** + * This class is a scanner generated by + * <a href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex</a> 1.2.2 + * on 4/6/04 11:13 PM from the specification file + * <tt>file:/D:/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.jflex</tt> + */ +public class XMLHeadTokenizer { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int YYINITIAL = 0; + final public static int UnDelimitedString = 10; + final public static int DQ_STRING = 6; + final public static int SQ_STRING = 8; + final public static int ST_XMLDecl = 2; + final public static int QuotedAttributeValue = 4; + + /** + * YY_LEXSTATE[l] is the state in the DFA for the lexical state l + * YY_LEXSTATE[l+1] is the state in the DFA for the lexical state l + * at the beginning of a line + * l is of the form l = 2*k, k a non negative integer + */ + private final static int YY_LEXSTATE[] = { + 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6 + }; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = + "\11\0\1\6\1\7\2\0\1\11\22\0\1\6\1\0\1\27\2\0"+ + "\1\31\1\0\1\30\24\0\1\12\1\10\1\26\1\13\3\0\1\21"+ + "\1\23\1\17\1\0\1\25\1\0\1\24\2\0\1\16\1\15\1\20"+ + "\1\22\10\0\1\14\12\0\1\21\1\23\1\17\1\0\1\25\1\0"+ + "\1\24\2\0\1\16\1\15\1\20\1\22\10\0\1\14\102\0\1\4"+ + "\3\0\1\5\17\0\1\3\16\0\1\1\20\0\1\3\16\0\1\1"+ + "\1\2\170\0\1\2\ufe87\0"; + + /** + * Translates characters to character classes + */ + final private static char [] yycmap = yy_unpack_cmap(yycmap_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", + "Internal error: unknown state", + "Error: could not match input", + "Error: pushback value was too large" + }; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + + + private boolean hasMore = true; + private final static int MAX_TO_SCAN = 8000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + + + public XMLHeadTokenizer() { + super(); + } + + public void reset (Reader in) { + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start + * of the matched text + */ + yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning + * of a line + */ + yy_atBOL = false; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + fStateStack.clear(); + + hasMore = true; + + // its a little wasteful to "throw away" first char array generated + // by class init (via auto generated code), but we really do want + // a small buffer for our head parsers. + if (yy_buffer.length != MAX_TO_SCAN) { + yy_buffer = new char[MAX_TO_SCAN]; + } + + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + context = primGetNextToken(); + HeadParserToken result = null; + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + + private void popState() { + yybegin(fStateStack.pop()); + } + + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + public XMLHeadTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public XMLHeadTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] yy_unpack_cmap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 128) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning. + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + System.out.println(YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println(YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + + System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + private void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + hasMore=false; + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + + while (true) { + + yychar+= yylength(); + + yy_atBOL = yy_markedPos <= 0 || yy_buffer[yy_markedPos-1] == '\n'; + if (!yy_atBOL && yy_buffer[yy_markedPos-1] == '\r') { + yy_atBOL = yy_advance() != '\n'; + if (!yy_atEOF) yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + if (yy_atBOL) + yy_state = YY_LEXSTATE[yy_lexical_state+1]; + else + yy_state = YY_LEXSTATE[yy_lexical_state]; + + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + + yy_input = yycmap[yy_input]; + + boolean yy_isFinal = false; + boolean yy_noLookAhead = false; + + yy_forNext: { switch (yy_state) { + case 0: + switch (yy_input) { + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 7; break yy_forNext; + } + + case 1: + switch (yy_input) { + case 1: yy_isFinal = true; yy_state = 8; break yy_forNext; + case 2: yy_isFinal = true; yy_state = 9; break yy_forNext; + case 3: yy_isFinal = true; yy_state = 10; break yy_forNext; + case 6: + case 7: + case 9: yy_isFinal = true; yy_state = 11; break yy_forNext; + case 10: yy_isFinal = true; yy_state = 12; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 7; break yy_forNext; + } + + case 2: + switch (yy_input) { + case 11: yy_isFinal = true; yy_state = 13; break yy_forNext; + case 15: yy_isFinal = true; yy_state = 14; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 7; break yy_forNext; + } + + case 3: + switch (yy_input) { + case 6: + case 9: yy_isFinal = true; yy_state = 16; break yy_forNext; + case 7: yy_isFinal = true; yy_state = 17; break yy_forNext; + case 23: yy_isFinal = true; yy_noLookAhead = true; yy_state = 18; break yy_forNext; + case 24: yy_isFinal = true; yy_noLookAhead = true; yy_state = 19; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 15; break yy_forNext; + } + + case 4: + switch (yy_input) { + case 7: + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 21; break yy_forNext; + case 11: yy_isFinal = true; yy_state = 22; break yy_forNext; + case 23: yy_isFinal = true; yy_noLookAhead = true; yy_state = 23; break yy_forNext; + case 24: yy_isFinal = true; yy_state = 24; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 5: + switch (yy_input) { + case 7: + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 21; break yy_forNext; + case 24: yy_isFinal = true; yy_state = 25; break yy_forNext; + case 25: yy_isFinal = true; yy_state = 26; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 6: + switch (yy_input) { + case 11: yy_isFinal = true; yy_state = 26; break yy_forNext; + case 6: + case 7: + case 9: yy_isFinal = true; yy_noLookAhead = true; yy_state = 27; break yy_forNext; + case 23: yy_isFinal = true; yy_noLookAhead = true; yy_state = 28; break yy_forNext; + case 24: yy_isFinal = true; yy_state = 29; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 20; break yy_forNext; + } + + case 8: + switch (yy_input) { + case 2: yy_isFinal = true; yy_noLookAhead = true; yy_state = 30; break yy_forNext; + default: break yy_forAction; + } + + case 9: + switch (yy_input) { + case 1: yy_isFinal = true; yy_noLookAhead = true; yy_state = 31; break yy_forNext; + default: break yy_forAction; + } + + case 10: + switch (yy_input) { + case 4: yy_state = 32; break yy_forNext; + default: break yy_forAction; + } + + case 11: + switch (yy_input) { + case 6: + case 7: + case 9: yy_state = 33; break yy_forNext; + case 10: yy_state = 34; break yy_forNext; + default: break yy_forAction; + } + + case 12: + switch (yy_input) { + case 11: yy_state = 35; break yy_forNext; + default: break yy_forAction; + } + + case 13: + switch (yy_input) { + case 22: yy_isFinal = true; yy_noLookAhead = true; yy_state = 36; break yy_forNext; + default: break yy_forAction; + } + + case 14: + switch (yy_input) { + case 16: yy_state = 37; break yy_forNext; + default: break yy_forAction; + } + + case 16: + switch (yy_input) { + case 6: + case 9: yy_isFinal = true; yy_state = 16; break yy_forNext; + case 7: yy_state = 38; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 15; break yy_forNext; + } + + case 17: + switch (yy_input) { + case 6: + case 9: yy_isFinal = true; yy_state = 16; break yy_forNext; + case 7: yy_state = 38; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 15; break yy_forNext; + } + + case 22: + switch (yy_input) { + case 22: yy_isFinal = true; yy_noLookAhead = true; yy_state = 39; break yy_forNext; + default: break yy_forAction; + } + + case 24: + switch (yy_input) { + case 10: yy_state = 40; break yy_forNext; + default: break yy_forAction; + } + + case 25: + switch (yy_input) { + case 10: yy_state = 40; break yy_forNext; + default: break yy_forAction; + } + + case 26: + switch (yy_input) { + case 22: yy_isFinal = true; yy_noLookAhead = true; yy_state = 41; break yy_forNext; + default: break yy_forAction; + } + + case 29: + switch (yy_input) { + case 10: yy_state = 40; break yy_forNext; + default: break yy_forAction; + } + + case 32: + switch (yy_input) { + case 5: yy_isFinal = true; yy_noLookAhead = true; yy_state = 42; break yy_forNext; + default: break yy_forAction; + } + + case 33: + switch (yy_input) { + case 6: + case 7: + case 9: yy_state = 33; break yy_forNext; + case 10: yy_state = 34; break yy_forNext; + default: break yy_forAction; + } + + case 34: + switch (yy_input) { + case 11: yy_state = 35; break yy_forNext; + default: break yy_forAction; + } + + case 35: + switch (yy_input) { + case 12: yy_state = 43; break yy_forNext; + default: break yy_forAction; + } + + case 37: + switch (yy_input) { + case 17: yy_state = 44; break yy_forNext; + default: break yy_forAction; + } + + case 38: + switch (yy_input) { + case 6: + case 9: yy_isFinal = true; yy_state = 16; break yy_forNext; + case 7: yy_state = 38; break yy_forNext; + default: yy_isFinal = true; yy_noLookAhead = true; yy_state = 15; break yy_forNext; + } + + case 40: + switch (yy_input) { + case 24: yy_isFinal = true; yy_noLookAhead = true; yy_state = 21; break yy_forNext; + default: break yy_forAction; + } + + case 43: + switch (yy_input) { + case 13: yy_state = 45; break yy_forNext; + default: break yy_forAction; + } + + case 44: + switch (yy_input) { + case 18: yy_state = 46; break yy_forNext; + default: break yy_forAction; + } + + case 45: + switch (yy_input) { + case 14: yy_state = 47; break yy_forNext; + default: break yy_forAction; + } + + case 46: + switch (yy_input) { + case 19: yy_state = 48; break yy_forNext; + default: break yy_forAction; + } + + case 47: + switch (yy_input) { + case 6: + case 7: + case 9: yy_isFinal = true; yy_state = 49; break yy_forNext; + default: break yy_forAction; + } + + case 48: + switch (yy_input) { + case 20: yy_state = 50; break yy_forNext; + default: break yy_forAction; + } + + case 49: + switch (yy_input) { + case 6: + case 7: + case 9: yy_isFinal = true; yy_state = 49; break yy_forNext; + default: break yy_forAction; + } + + case 50: + switch (yy_input) { + case 16: yy_state = 51; break yy_forNext; + default: break yy_forAction; + } + + case 51: + switch (yy_input) { + case 21: yy_state = 52; break yy_forNext; + default: break yy_forAction; + } + + case 52: + switch (yy_input) { + case 6: + case 7: + case 9: yy_state = 52; break yy_forNext; + case 8: yy_isFinal = true; yy_state = 53; break yy_forNext; + default: break yy_forAction; + } + + case 53: + switch (yy_input) { + case 6: + case 7: + case 9: yy_isFinal = true; yy_state = 53; break yy_forNext; + default: break yy_forAction; + } + + default: + yy_ScanError(YY_ILLEGAL_STATE); + break; + } } + + if ( yy_isFinal ) { + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if ( yy_noLookAhead ) break yy_forAction; + } + + } + } + + + switch (yy_action) { + + case 25: + { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + case 55: break; + case 21: + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 56: break; + case 15: + case 16: + { yypushback(1); yybegin(UnDelimitedString); string.setLength(0); } + case 57: break; + case 28: + case 29: + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue; } + case 58: break; + case 39: + { yypushback(2); popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 59: break; + case 41: + { yypushback(2);popState(); valueText = string.toString(); return EncodingParserConstants.InvalidTerminatedStringValue; } + case 60: break; + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 17: + { if (yychar > MAX_TO_SCAN) {hasMore=false; return EncodingParserConstants.MAX_CHARS_REACHED;} } + case 61: break; + case 30: + { if (yychar == 0 ) {hasMore = false; return EncodingParserConstants.UTF16BE;} } + case 62: break; + case 31: + { if (yychar == 0 ) {hasMore = false; return EncodingParserConstants.UTF16LE;} } + case 63: break; + case 42: + { if (yychar == 0 ) {hasMore = false; return EncodingParserConstants.UTF83ByteBOM;} } + case 64: break; + case 49: + { if (yychar == 0 ) {yybegin(ST_XMLDecl); return XMLHeadTokenizerConstants.XMLDeclStart;} } + case 65: break; + case 36: + { yybegin(YYINITIAL); hasMore = false; return XMLHeadTokenizerConstants.XMLDeclEnd; } + case 66: break; + case 53: + { pushCurrentState(); yybegin(QuotedAttributeValue); return XMLHeadTokenizerConstants.XMLDelEncoding; } + case 67: break; + case 23: + { popState(); valueText = string.toString(); return EncodingParserConstants.StringValue; } + case 68: break; + case 20: + case 22: + case 24: + case 26: + { string.append( yytext() ); } + case 69: break; + case 19: + { yybegin(SQ_STRING); string.setLength(0); } + case 70: break; + case 18: + { yybegin(DQ_STRING); string.setLength(0); } + case 71: break; + case 27: + { yypushback(1);popState(); valueText = string.toString(); return EncodingParserConstants.UnDelimitedStringValue; } + case 72: break; + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + { hasMore = false; return EncodingParserConstants.EOF; } + } + else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + /** + * Runs the scanner on input files. + * + * This main method is the debugging routine for the scanner. + * It prints each returned token to System.out until the end of + * file is reached, or an error occured. + * + * @param argv the command line, contains the filenames to run + * the scanner on. + */ + public static void main(String argv[]) { + for (int i = 0; i < argv.length; i++) { + XMLHeadTokenizer scanner = null; + try { + scanner = new XMLHeadTokenizer( new java.io.FileReader(argv[i]) ); + } + catch (java.io.FileNotFoundException e) { + System.out.println("File not found : \""+argv[i]+"\""); + System.exit(1); + } + catch (java.io.IOException e) { + System.out.println("Error opening file \""+argv[i]+"\""); + System.exit(1); + } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Usage : java XMLHeadTokenizer <inputfile>"); + System.exit(1); + } + + try { + do { + System.out.println(scanner.primGetNextToken()); + } while (!scanner.yy_atEOF); + + } + catch (java.io.IOException e) { + System.out.println("An I/O error occured while scanning :"); + System.out.println(e); + System.exit(1); + } + catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizerGenJava.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizerGenJava.cmd new file mode 100644 index 0000000..7ec1c7b --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizerGenJava.cmd
@@ -0,0 +1,28 @@ +@echo on + +rem The following variables need to be set/specified for each "development machine" +set PATH=%PATH%;D:\JDKs\j2sdk1.4.2_03\bin +set WORKSPACE_LOCATION=D:\builds\Workspaces\WSWBM8 +set JFLEX_LIB_LOCATION=D:\DevTimeSupport\JFlex\lib + +rem The following variables differ from project to project, but should be otherwise constant +set MAIN_NAME=XMLHeadTokenizer + +rem set PROJECT_SRC=\org.eclipse.wst.sse.core.xml\src\ +rem set PACKAGE_DIR=com\ibm\sse\model\xml\encoding\ + +set PROJECT_SRC=\org.eclipse.wst.common.encoding.contentspecific\src\ +set PACKAGE_DIR=com\ibm\encoding\resource\contentspecific\xml\ + + +rem Given the above "framework" and the command themselves, these variables should never need to be modified +set JAVA_FILE=%MAIN_NAME%.java +set JFLEX_RULES=%MAIN_NAME%.jflex +set SKELETON_FILE=%MAIN_NAME%.skeleton + +IF EXIST %JAVA_FILE% del %JAVA_FILE% +rem java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\sed-jflex.jar;. JFlex.Main %JFLEX_RULES% -skel %SKELETON_FILE% 1>jflexout.txt 2>jflexerr.txt +java -Xmx470000000 -cp %JFLEX_LIB_LOCATION%\sed-jflex.jar;. JFlex.Main %JFLEX_RULES% 1>jflexout.txt 2>jflexerr.txt +IF EXIST %JAVA_FILE% copy %JAVA_FILE% %WORKSPACE_LOCATION%%PROJECT_SRC%%PACKAGE_DIR%%JAVA_FILE% + +pause
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/jflexerr.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/jflexerr.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/jflexerr.txt
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/jflexout.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/jflexout.txt new file mode 100644 index 0000000..316c32e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/jflexout.txt
@@ -0,0 +1,8 @@ +Reading "XMLHeadTokenizer.jflex" + +Warning : Macro "SpaceChar" has been declared but never used. +Constructing NFA : 358 states in NFA +Converting NFA to DFA : +................................................................... +79 states before minimization, 54 states in minimized DFA +Writing code to "XMLHeadTokenizer.java"
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/oldXMLHeadTokenizer.skeleton b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/oldXMLHeadTokenizer.skeleton new file mode 100644 index 0000000..9809602 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/HeadParsers/XMLHeadTokenizer/oldXMLHeadTokenizer.skeleton
@@ -0,0 +1,263 @@ + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ +--- lexical states, charmap + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", //$NON-NLS-1$ + "Internal error: unknown state", //$NON-NLS-1$ + "Error: could not match input", //$NON-NLS-1$ + "Error: pushback value was too large" //$NON-NLS-1$ + }; + +--- isFinal list + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + // state stack for handling embedded regions + private IntStack fStateStack = new IntStack(); + + +--- user class code + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ +--- constructor declaration + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning - from the SED JFlex skeleton + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + //Logger.log(Logger.ERROR, YY_ERROR_MSG[errorCode]); + System.out.println("ScanError: " + errorCode + ": " + YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + //Logger.log(Logger.ERROR, YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + System.out.println("ScanError: " + errorCode + ": " + YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + // DO NOT EXIT the VM on an error + // System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + private void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + +--- yy_doEof + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ +--- yylex declaration + int yy_input; + int yy_action; + +--- local declarations + + while (true) { + +--- start admin (line, char, col count) + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + +--- start admin (lexstate etc) + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + +--- line, col, char count, next transition, isFinal action + yy_action = yy_state; + yy_markedPos = yy_currentPos; +--- line count update + } + + } + } + +--- char count update + + switch (yy_action) { + +--- actions + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; +--- eofvalue + } + else { +--- no match + } + } + } + } + +--- main + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/CSSTokenizer/devel/CSSTokenizer.jflex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/CSSTokenizer/devel/CSSTokenizer.jflex new file mode 100644 index 0000000..4648a3f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/CSSTokenizer/devel/CSSTokenizer.jflex
@@ -0,0 +1,522 @@ +/******************************************************************************* + * Copyright (c) 2004, 2011 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 + *******************************************************************************/ +/*nlsXXX*/ +package org.eclipse.wst.css.core.internal.parser; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.wst.css.core.internal.parser.regions.CSSTextRegionFactory; +import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts; +import org.eclipse.wst.css.core.internal.parserz.CSSTextToken; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + +%% + +%public +%class CSSTokenizer +%implements CSSRegionContexts, ICSSTokenizer +%function primGetNextToken +%type String +%char +%unicode +%caseless +//%debug +%pack + +%{ + private final static String UNDEFINED = "undefined"; + private String fBufferedContext = null; + private int fBufferedStart; +// private int fBufferedTextLength; + private int fBufferedLength; +// private StringBuffer fBufferedText = null; + private CSSTextRegionFactory fRegionFactory = CSSTextRegionFactory.getInstance(); + private int fInitialState = YYINITIAL; + public final static int BUFFER_SIZE_NORMAL = 16384; + public final static int BUFFER_SIZE_SMALL = 256; + private int fInitialBufferSize = BUFFER_SIZE_NORMAL; + + public void setInitialState(int state) { + fInitialState = state; + } + + public void setInitialBufferSize(int size) { + fInitialBufferSize = size; + } + + /* user method */ + public final ITextRegion getNextToken() throws IOException { + String context; + String nextTokenType; + boolean spaceFollows; +// StringBuffer text; + int start; + int textLength; + int length; + if (fBufferedContext != null) { + context = fBufferedContext; +// text = fBufferedText; + start = fBufferedStart; + textLength = length = fBufferedLength; + + fBufferedContext = null; + } else { + context = primGetNextToken(); +// text = new StringBuffer(yytext()); + start = yychar; + textLength = length = yylength(); + } + + if (context != null) { + if (context == UNDEFINED) { + // undef -> concatenate undef's + nextTokenType = primGetNextToken(); + while (nextTokenType == UNDEFINED) { +// text.append(yytext()); + textLength += yylength(); + length = textLength; + nextTokenType = primGetNextToken(); + } + fBufferedContext = nextTokenType; +// fBufferedText = new StringBuffer(yytext()); + fBufferedStart = yychar; + fBufferedLength = yylength(); + } else { + nextTokenType = null; + spaceFollows = false; + if (CSSRegionUtil.isDeclarationValueType(context)) { // declaration value can contain VALUE_S + nextTokenType = primGetNextToken(); + spaceFollows = (nextTokenType == CSS_DECLARATION_VALUE_S); + } else if (canContainSpace(context)) { + nextTokenType = primGetNextToken(); + spaceFollows = (nextTokenType == CSS_S); + } + if (nextTokenType != null) { // nextToken is retrieved + if (spaceFollows && (context != CSS_COMMENT)) { + // next is space -> append +// text.append(yytext()); + length += yylength(); + } else { + // next is NOT space -> push this for next time, return itself + fBufferedContext = nextTokenType; +// fBufferedText = new StringBuffer(yytext()); + fBufferedStart = yychar; + fBufferedLength = yylength(); + } + } + } + } + + if (context != null) { + if (context == UNDEFINED) { + context = CSS_UNKNOWN; + } + return fRegionFactory.createRegion(context, start, textLength, length); + } else { + return null; + } + } + + /* user method */ + /* for standalone use */ + public final List parseText() throws IOException { + List tokens = new ArrayList(); + + CSSTextToken token; + for (String kind = primGetNextToken(); kind != null; kind = primGetNextToken()) { + token = new CSSTextToken(); + token.kind = kind; + token.start = yychar; + token.length = yylength(); + token.image = yytext(); + tokens.add(token); + } + + return tokens; + } + + /* user method */ + private boolean canContainSpace(String type) { + if (type == CSS_DELIMITER || type == CSS_RBRACE || type == CSS_DECLARATION_DELIMITER) { + return false; + } else { + return true; + } + } + + /* user method */ + public final int getOffset() { + return yychar; + } + + /* user method */ + public final boolean isEOF() { + return zzAtEOF; + } + + /* user method */ + public void reset(char[] charArray) { + reset(new CharArrayReader(charArray), 0); + } + + /* user method */ + public final void reset(java.io.Reader in, int newOffset) { + /** the input device */ + zzReader = in; + + /** the current state of the DFA */ + zzState = 0; + + /** the current lexical state */ + zzLexicalState = fInitialState; //YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + if (zzBuffer.length != fInitialBufferSize) { + zzBuffer = new char[fInitialBufferSize]; + } + java.util.Arrays.fill(zzBuffer, (char)0); + + /** the textposition at the last accepting state */ + zzMarkedPos = 0; + + /** the textposition at the last state to be included in yytext */ +// yy_pushbackPos = 0; + + /** the current text position in the buffer */ + zzCurrentPos = 0; + + /** startRead marks the beginning of the yytext() string in the buffer */ + zzStartRead = 0; + + /** endRead marks the last character in the buffer, that has been read + from input */ + zzEndRead = 0; + + /** number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /** the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ +// yycolumn = 0; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ +// yy_atBOL = false; + + /** zzAtEOF == true <=> the scanner has returned a value for EOF */ + zzAtEOF = false; + + /* user variables */ + // fUndefined.delete(0, fUndefined.length()); + } + + /* user method */ + public CSSTokenizer() { + super(); + } + + /** + * Added to workaround stricter compilation options without creating + * an alternate skeleton file + */ + void _usePrivates() { + System.out.print(yycolumn); + System.out.print(yyline); + System.out.print(Boolean.toString(zzAtBOL)); + } +%} + +%state ST_CHARSET_NAME +%state ST_CHARSET_DELIMITER +%state ST_IMPORT_URI +%state ST_IMPORT_MEDIUM +%state ST_IMPORT_DELIMITER +%state ST_MEDIA_MEDIUM +%state ST_MEDIA_DELIMITER +%state ST_PAGE_PSEUDO_PAGE +%state ST_PAGE_DELIMITER +%state ST_FONT_FACE_DELIMITER +%state ST_SELECTOR +%state ST_SELECTOR_MODIFIER +%state ST_SELECTOR_ATTRIBUTE_NAME +%state ST_SELECTOR_ATTRIBUTE_OPERATOR +%state ST_SELECTOR_ATTRIBUTE_VALUE +%state ST_SELECTOR_ATTRIBUTE_END +%state ST_DECLARATION +%state ST_DECLARATION_SEPARATOR +%state ST_DECLARATION_PRE_VALUE +%state ST_DECLARATION_VALUE + +h = [0-9a-f] +nonascii = [\u0080-\uffff] +unicode = \\{h}{1,6}[ \t\r\n\f]? +escape = {unicode}|\\[ -~\u0080-\uffff] + + + +nmstart = [_a-zA-Z]|{nonascii}|{escape} +nmchar = [_a-zA-Z0-9-]|{nonascii}|{escape} +string1 = \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\" +string2 = \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\' + +ident = -?{nmstart}{nmchar}* +value_ident = -?{nmstart}"."?({nmchar}+"."?)* + +name = {nmchar}+ +num = [+-]?([0-9]+|[0-9]*"."[0-9]+) +string = {string1}|{string2} +url = ([ !#$%&*-~]|{nonascii}|{escape})* +s = [ \t\r\n\f] +w = {s}* +nl = \n|\r\n|\r|\f + +//range = \?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h}))))) + +hash = "#"{name} +uri = ("url("{w}{string}{w}")"|"url("{w}{url}{w}")") +function = {ident}"(" +unicode_range = "U"\+[0-9a-fA-F?]{1,6}("-"[0-9a-fA-F?]{1,6})? + +%% + +/* + * *** global *** + */ + +{s}+ { return CSS_S; } +"<!--" { return CSS_CDO; } +"-->" { return CSS_CDC; } +"}" { yybegin(YYINITIAL); return CSS_RBRACE; } +\/\*[^*]*\*+([^/*][^*]*\*+)*\/ { return CSS_COMMENT; } + +//<YYINITIAL> { +// "@import" { yybegin(ST_IMPORT_URI); return CSS_IMPORT; } +//} + +/* + * *** charset rule *** + * CHARSET_SYM S* STRING S* ';' + */ + +"@charset" { yybegin(ST_CHARSET_NAME); return CSS_CHARSET; } + +<ST_CHARSET_NAME> { + {string} { yybegin(ST_CHARSET_DELIMITER); return CSS_STRING; } +} + +<ST_CHARSET_DELIMITER> { + ";" { yybegin(YYINITIAL); return CSS_DELIMITER; } +} + +/* + * *** import rule *** + * IMPORT_SYM S* [STRING|URI] S* [ medium [ COMMA S* medium]* ]? ';' S* + */ + +"@import" { yybegin(ST_IMPORT_URI); return CSS_IMPORT; } + +<ST_IMPORT_URI> { + {string} { yybegin(ST_IMPORT_MEDIUM); return CSS_STRING; } + // "url("{w}{string}{w}")" { yybegin(ST_IMPORT_MEDIUM); return CSS_URI; } + // "url("{w}{url}{w}")" { yybegin(ST_IMPORT_MEDIUM); return CSS_URI; } + {uri} { yybegin(ST_IMPORT_MEDIUM); return CSS_URI; } + ";" { yybegin(YYINITIAL); return CSS_DELIMITER; } +} + +<ST_IMPORT_MEDIUM> { + {ident} { yybegin(ST_IMPORT_DELIMITER); return CSS_MEDIUM; } + ";" { yybegin(YYINITIAL); return CSS_DELIMITER; } +} + +<ST_IMPORT_DELIMITER> { + ";" { yybegin(YYINITIAL); return CSS_DELIMITER; } + "," { yybegin(ST_IMPORT_MEDIUM); return CSS_MEDIA_SEPARATOR; } +} + +/* + * *** media rule *** + * MEDIA_SYM S* medium [ COMMA S* medium ]* LBRACE S* ruleset* '}' S* + */ + +"@media" { yybegin(ST_MEDIA_MEDIUM); return CSS_MEDIA; } + +/* + * medium + * IDENT S* + */ +<ST_MEDIA_MEDIUM> { + {ident} { yybegin(ST_MEDIA_DELIMITER); return CSS_MEDIUM; } + "{" { yybegin(YYINITIAL); return CSS_LBRACE; } +} + +<ST_MEDIA_DELIMITER> { + "{" { yybegin(YYINITIAL); return CSS_LBRACE; } + "," { yybegin(ST_MEDIA_MEDIUM); return CSS_MEDIA_SEPARATOR; } +} + +/* + * *** page rule ** + * PAGE_SYM S* pseudo_page? S* LBRACE S* declaration [ ';' S* declaration ]* '}' S* + */ + +"@page" { yybegin(ST_PAGE_PSEUDO_PAGE); return CSS_PAGE; } + +/* + * pseudo_page + * ':' IDENT + */ + +<ST_PAGE_PSEUDO_PAGE> { + ":"?{ident} { yybegin(ST_PAGE_DELIMITER); return CSS_PAGE_SELECTOR; } + "{" { yybegin(ST_DECLARATION); return CSS_LBRACE; } +} + +<ST_PAGE_DELIMITER> { + "{" { yybegin(ST_DECLARATION); return CSS_LBRACE; } +} + +/* + * font-face + * FONT_FACE_SYM S* '{' S* declaration [ ';' S* declaration '* '}' S* + */ + +"@font-face" { yybegin(ST_FONT_FACE_DELIMITER); return CSS_FONT_FACE; } + +<ST_FONT_FACE_DELIMITER> { + "{" { yybegin(ST_DECLARATION); return CSS_LBRACE; } +} + +/* + * selector + * simple_selector [ combinator simple_selector ]* + */ + +/* + * simple_selector + * element_name [ HASH | class | attrib | pseudo ]* | [ HASH | class | attrib | pseudo ]+ + */ + +<YYINITIAL, ST_SELECTOR_MODIFIER, ST_SELECTOR> { + "*" { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_UNIVERSAL; } + {hash} { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_ID; } +// ":"{ident}("("{s}*{ident}{s}*")")? { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_PSEUDO; } + ":"({ident}("("{s}*([a-zA-Z0-9]|[-+]|{s})*{s}*")")?)? { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_PSEUDO; } + "."{name} { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_CLASS; } + "[" { yybegin(ST_SELECTOR_ATTRIBUTE_NAME); return CSS_SELECTOR_ATTRIBUTE_START; } +} + +<YYINITIAL, ST_SELECTOR> { + {ident} { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_ELEMENT_NAME; } +} + +<ST_SELECTOR_MODIFIER> { + "," { yybegin(ST_SELECTOR); return CSS_SELECTOR_SEPARATOR; } + // using LOOKAHEAD + {s}+/[^+>\{,/] { yybegin(ST_SELECTOR); return CSS_SELECTOR_COMBINATOR; } + "+"|">" { yybegin(ST_SELECTOR); return CSS_SELECTOR_COMBINATOR; } + "{" { yybegin(ST_DECLARATION); return CSS_LBRACE; } +} + +/* + * attrib + * '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* [ IDENT | STRING ] S* ]? ']' + */ + +<ST_SELECTOR_ATTRIBUTE_NAME> { + {ident} { yybegin(ST_SELECTOR_ATTRIBUTE_OPERATOR); return CSS_SELECTOR_ATTRIBUTE_NAME; } + "]" { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_ATTRIBUTE_END; } +} + +<ST_SELECTOR_ATTRIBUTE_OPERATOR> { + "="|"~="|"|="|"*="|"$="|"^=" { yybegin(ST_SELECTOR_ATTRIBUTE_VALUE); return CSS_SELECTOR_ATTRIBUTE_OPERATOR; } + "]" { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_ATTRIBUTE_END; } +} + +<ST_SELECTOR_ATTRIBUTE_VALUE> { + {ident}|{string} { yybegin(ST_SELECTOR_ATTRIBUTE_END); return CSS_SELECTOR_ATTRIBUTE_VALUE; } +} + +<ST_SELECTOR_ATTRIBUTE_END> { + "]" { yybegin(ST_SELECTOR_MODIFIER); return CSS_SELECTOR_ATTRIBUTE_END; } +} + +/* + * declaration + * property ':' S* expr prio? | // empty // + */ + +<ST_DECLARATION> { + \x2A?{ident} { yybegin(ST_DECLARATION_SEPARATOR); return CSS_DECLARATION_PROPERTY; } +} + +<ST_DECLARATION_SEPARATOR> { + ":" { yybegin(ST_DECLARATION_PRE_VALUE); return CSS_DECLARATION_SEPARATOR; } +} + +<ST_DECLARATION_PRE_VALUE, ST_DECLARATION_VALUE> { + "!"{s}*"important" { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_IMPORTANT; } + + + ")" { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_PARENTHESIS_CLOSE; } + + // ordered following two rules deliberately, see + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=129902 + {num}{ident} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_DIMENSION; } + {value_ident} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_IDENT; } + + + {num}"%" { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_PERCENTAGE; } + + {num} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_NUMBER; } + + + + {function} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_FUNCTION; } + {string} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_STRING; } + {uri} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_URI; } + "#"{name} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_HASH; } + {unicode_range} { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_UNICODE_RANGE; } + [,/] { yybegin(ST_DECLARATION_VALUE); return CSS_DECLARATION_VALUE_OPERATOR; } +} + +<ST_DECLARATION_VALUE> { + {s}+/[^;}] { return CSS_DECLARATION_VALUE_S; } +} + +<ST_DECLARATION, ST_DECLARATION_SEPARATOR, ST_DECLARATION_PRE_VALUE, ST_DECLARATION_VALUE> { + ";" { yybegin(ST_DECLARATION); return CSS_DECLARATION_DELIMITER; } + // "}" { yybegin(YYINITIAL); return CSS_RBRACE; } +} + + +//<YYINITIAL, ST_IMPORT_URI, ST_IMPORT_MEDIUM, ST_IMPORT_DELIMITER> { +// \/\*[^*]*\*+([^/*][^*]*\*+)*\/ { return CSS_COMMENT; } +// {s}+ { return CSS_S; } +// . { return UNDEFINED; } +//} + +//<YYINITIAL, ST_IMPORT_URI, ST_IMPORT_MEDIUM, ST_IMPORT_DELIMITER> { +// [^ \t\r\n\f]+ { return CSS_UNKNOWN; } +//} + +. { + return UNDEFINED; +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/CSSTokenizer/devel/flex.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/CSSTokenizer/devel/flex.cmd new file mode 100644 index 0000000..d39ae78 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/CSSTokenizer/devel/flex.cmd
@@ -0,0 +1,6 @@ +@echo off +set JAVA_HOME=d:\jdk6_03 +set JFLEX_HOME=D:\JFlex\jflex-1.4.2 + +%JAVA_HOME%\bin\java -Xmx470M -jar %JFLEX_HOME%\lib\JFlex.jar CSSTokenizer.jflex +move CSSTokenizer.java ..\..\..\..\..\org.eclipse.wst.css.core\src\org\eclipse\wst\css\core\internal\parser\ && del CSSTokenizer.java*
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/README b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/README new file mode 100644 index 0000000..3d820e1 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/README
@@ -0,0 +1,9 @@ +devel - the active development environment (sans JDK) + - latest version of HTMLTokenizer specification and generated code + - modified skeleton to prevent VM exits on unmatched input + - "flex" scripts to run JFlex with modified skeleton + - active RCS archive +resources - backup/pristine resources + - Unmodified JFlex 1.2.2 download + - Separate modifications to the JFlex skeleton + - W3C XML recommendation used for several of the HTMLTokenizer rules
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.java b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.java new file mode 100644 index 0000000..b424476 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.java
@@ -0,0 +1,3838 @@ +/* The following code was generated by JFlex 1.2.2 on 10/24/07 5:16 AM */ + +/******************************************************************************* + * Copyright (c) 2004, 2007 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.jst.jsp.core.internal.parser.internal; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jst.jsp.core.internal.Logger; +import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTokenizer; +import org.eclipse.wst.sse.core.internal.ltk.parser.TagMarker; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.eclipse.wst.xml.core.internal.parser.ContextRegionContainer; +import org.eclipse.wst.xml.core.internal.parser.IntStack; + + +/** + * This class is a scanner generated by + * <a href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex</a> 1.2.2 + * on 10/24/07 5:16 AM from the specification file + * <tt>file:/D:/eclipse.wtp/workspace/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.jflex</tt> + */ +public class JSPTokenizer implements BlockTokenizer, DOMJSPRegionContexts { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int ST_JSP_VBL_DQUOTES = 51; + final public static int ST_JSP_VBL_SQUOTES = 50; + final public static int ST_JSP_VBL_SQUOTES_END = 52; + final public static int ST_XML_COMMENT_END = 4; + final public static int ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE = 21; + final public static int ST_JSP_EL_SQUOTES_END = 45; + final public static int ST_JSP_EL_DQUOTES = 44; + final public static int ST_JSP_EL = 42; + final public static int ST_BLOCK_TAG_SCAN = 36; + final public static int ST_JSP_EL_SQUOTES = 43; + final public static int ST_DHTML_ATTRIBUTE_VALUE = 14; + final public static int ST_XML_PI_ATTRIBUTE_NAME = 8; + final public static int ST_DHTML_TAG_CLOSE = 15; + final public static int ST_XML_ATTRIBUTE_VALUE_DQUOTED = 41; + final public static int ST_DHTML_EQUALS = 13; + final public static int ST_XML_PI_ATTRIBUTE_VALUE = 10; + final public static int ST_XML_ATTRIBUTE_VALUE = 25; + final public static int ST_JSP_VBL = 49; + final public static int ST_JSP_SQUOTED_VBL = 55; + final public static int ST_XML_ATTRIBUTE_VALUE_SQUOTED = 40; + final public static int ST_XML_ATTRIBUTE_NAME = 23; + final public static int ST_XML_EQUALS = 24; + final public static int YYINITIAL = 0; + final public static int ST_JSP_DIRECTIVE_ATTRIBUTE_NAME = 19; + final public static int ST_JSP_CONTENT = 16; + final public static int ST_XML_DOCTYPE_ID_SYSTEM = 31; + final public static int ST_XML_ELEMENT_DECLARATION = 32; + final public static int ST_XML_DECLARATION_CLOSE = 27; + final public static int ST_JSP_DIRECTIVE_EQUALS = 20; + final public static int ST_JSP_VBL_DQUOTES_END = 53; + final public static int ST_JSP_DQUOTED_EL = 47; + final public static int ST_XML_DOCTYPE_DECLARATION = 28; + final public static int ST_CDATA_END = 2; + final public static int ST_PI_WS = 6; + final public static int ST_CDATA_TEXT = 1; + final public static int ST_JSP_DIRECTIVE_NAME_WHITESPACE = 18; + final public static int ST_XML_ELEMENT_DECLARATION_CONTENT = 33; + final public static int ST_XML_ATTLIST_DECLARATION = 34; + final public static int ST_JSP_EL_DQUOTES_END = 46; + final public static int ST_JSP_SQUOTED_EL = 48; + final public static int ST_JSP_COMMENT_END = 39; + final public static int ST_XML_PI_EQUALS = 9; + final public static int ST_XML_ATTLIST_DECLARATION_CONTENT = 35; + final public static int ST_XML_DOCTYPE_ID_PUBLIC = 30; + final public static int ST_JSP_DQUOTED_VBL = 54; + final public static int ST_DHTML_ATTRIBUTE_NAME = 12; + final public static int ST_ABORT_EMBEDDED = 37; + final public static int ST_XML_DOCTYPE_EXTERNAL_ID = 29; + final public static int ST_JSP_COMMENT = 38; + final public static int ST_PI_CONTENT = 7; + final public static int ST_BLOCK_TAG_INTERNAL_SCAN = 37; + final public static int ST_PI = 5; + final public static int ST_XML_DECLARATION = 26; + final public static int ST_JSP_DIRECTIVE_NAME = 17; + final public static int ST_XML_TAG_NAME = 22; + final public static int ST_XML_PI_TAG_CLOSE = 11; + final public static int ST_XML_COMMENT = 3; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = + "\11\0\1\5\1\26\2\0\1\17\22\0\1\17\1\25\1\12\1\65"+ + "\1\15\1\22\1\13\1\14\1\24\1\24\1\24\1\24\1\24\1\7"+ + "\1\6\1\3\12\20\1\11\1\72\1\1\1\51\1\2\1\4\1\21"+ + "\1\40\1\73\1\36\1\37\1\55\1\70\1\62\1\62\1\63\1\62"+ + "\1\62\1\33\1\31\1\64\1\52\1\54\1\62\1\67\1\66\1\41"+ + "\1\71\2\62\1\27\1\53\1\62\1\35\1\104\1\23\1\0\1\10"+ + "\1\0\1\57\1\103\1\74\1\60\1\43\1\70\1\75\1\106\1\46"+ + "\1\100\1\62\1\34\1\32\1\50\1\47\1\101\1\62\1\44\1\45"+ + "\1\56\1\102\1\42\1\62\1\30\1\61\1\62\1\16\1\0\1\105"+ + "\71\0\1\77\10\0\27\76\1\0\37\76\1\0\72\76\2\0\13\76"+ + "\2\0\10\76\1\0\65\76\1\0\104\76\11\0\44\76\3\0\2\76"+ + "\4\0\36\76\70\0\131\76\22\0\7\76\16\0\2\77\56\0\106\77"+ + "\32\0\2\77\44\0\1\76\1\77\3\76\1\0\1\76\1\0\24\76"+ + "\1\0\54\76\1\0\7\76\3\0\1\76\1\0\1\76\1\0\1\76"+ + "\1\0\1\76\1\0\22\76\15\0\14\76\1\0\102\76\1\0\14\76"+ + "\1\0\44\76\1\0\4\77\11\0\65\76\2\0\2\76\2\0\2\76"+ + "\3\0\34\76\2\0\10\76\2\0\2\76\67\0\46\76\2\0\1\76"+ + "\7\0\46\76\12\0\21\77\1\0\27\77\1\0\3\77\1\0\1\77"+ + "\1\0\2\77\1\0\1\77\13\0\33\76\5\0\3\76\56\0\32\76"+ + "\5\0\1\77\12\76\10\77\15\0\12\77\6\0\1\77\107\76\2\0"+ + "\5\76\1\0\17\76\1\0\4\76\1\0\1\76\17\77\2\76\2\77"+ + "\1\0\4\77\2\0\12\77\u0207\0\3\77\1\0\65\76\2\0\1\77"+ + "\1\76\20\77\3\0\4\77\3\0\12\76\2\77\2\0\12\77\21\0"+ + "\3\77\1\0\10\76\2\0\2\76\2\0\26\76\1\0\7\76\1\0"+ + "\1\76\3\0\4\76\2\0\1\77\1\0\7\77\2\0\2\77\2\0"+ + "\3\77\11\0\1\77\4\0\2\76\1\0\3\76\2\77\2\0\12\77"+ + "\2\76\20\0\1\77\2\0\6\76\4\0\2\76\2\0\26\76\1\0"+ + "\7\76\1\0\2\76\1\0\2\76\1\0\2\76\2\0\1\77\1\0"+ + "\5\77\4\0\2\77\2\0\3\77\13\0\4\76\1\0\1\76\7\0"+ + "\12\77\2\77\3\76\14\0\3\77\1\0\7\76\1\0\1\76\1\0"+ + "\3\76\1\0\26\76\1\0\7\76\1\0\2\76\1\0\5\76\2\0"+ + "\1\77\1\76\10\77\1\0\3\77\1\0\3\77\22\0\1\76\5\0"+ + "\12\77\21\0\3\77\1\0\10\76\2\0\2\76\2\0\26\76\1\0"+ + "\7\76\1\0\2\76\2\0\4\76\2\0\1\77\1\76\6\77\3\0"+ + "\2\77\2\0\3\77\10\0\2\77\4\0\2\76\1\0\3\76\4\0"+ + "\12\77\22\0\2\77\1\0\6\76\3\0\3\76\1\0\4\76\3\0"+ + "\2\76\1\0\1\76\1\0\2\76\3\0\2\76\3\0\3\76\3\0"+ + "\10\76\1\0\3\76\4\0\5\77\3\0\3\77\1\0\4\77\11\0"+ + "\1\77\17\0\11\77\21\0\3\77\1\0\10\76\1\0\3\76\1\0"+ + "\27\76\1\0\12\76\1\0\5\76\4\0\7\77\1\0\3\77\1\0"+ + "\4\77\7\0\2\77\11\0\2\76\4\0\12\77\22\0\2\77\1\0"+ + "\10\76\1\0\3\76\1\0\27\76\1\0\12\76\1\0\5\76\4\0"+ + "\7\77\1\0\3\77\1\0\4\77\7\0\2\77\7\0\1\76\1\0"+ + "\2\76\4\0\12\77\22\0\2\77\1\0\10\76\1\0\3\76\1\0"+ + "\27\76\1\0\20\76\4\0\6\77\2\0\3\77\1\0\4\77\11\0"+ + "\1\77\10\0\2\76\4\0\12\77\221\0\56\76\1\0\1\76\1\77"+ + "\2\76\7\77\5\0\6\76\1\77\10\77\1\0\12\77\47\0\2\76"+ + "\1\0\1\76\2\0\2\76\1\0\1\76\2\0\1\76\6\0\4\76"+ + "\1\0\7\76\1\0\3\76\1\0\1\76\1\0\1\76\2\0\2\76"+ + "\1\0\2\76\1\0\1\76\1\77\2\76\6\77\1\0\2\77\1\76"+ + "\2\0\5\76\1\0\1\77\1\0\6\77\2\0\12\77\76\0\2\77"+ + "\6\0\12\77\13\0\1\77\1\0\1\77\1\0\1\77\4\0\2\77"+ + "\10\76\1\0\41\76\7\0\24\77\1\0\6\77\4\0\6\77\1\0"+ + "\1\77\1\0\25\77\3\0\7\77\1\0\1\77\346\0\46\76\12\0"+ + "\47\76\11\0\1\76\1\0\2\76\1\0\3\76\1\0\1\76\1\0"+ + "\2\76\1\0\5\76\51\0\1\76\1\0\1\76\1\0\1\76\13\0"+ + "\1\76\1\0\1\76\1\0\1\76\3\0\2\76\3\0\1\76\5\0"+ + "\3\76\1\0\1\76\1\0\1\76\1\0\1\76\1\0\1\76\3\0"+ + "\2\76\3\0\2\76\1\0\1\76\50\0\1\76\11\0\1\76\2\0"+ + "\1\76\2\0\2\76\7\0\2\76\1\0\1\76\1\0\7\76\50\0"+ + "\1\76\4\0\1\76\10\0\1\76\u0c06\0\234\76\4\0\132\76\6\0"+ + "\26\76\2\0\6\76\2\0\46\76\2\0\6\76\2\0\10\76\1\0"+ + "\1\76\1\0\1\76\1\0\1\76\1\0\37\76\2\0\65\76\1\0"+ + "\7\76\1\0\1\76\3\0\3\76\1\0\7\76\3\0\4\76\2\0"+ + "\6\76\4\0\15\76\5\0\3\76\1\0\7\76\323\0\15\77\4\0"+ + "\1\77\104\0\1\76\3\0\2\76\2\0\1\76\121\0\3\76\u0e82\0"+ + "\1\77\1\0\1\76\31\0\11\76\6\77\1\0\5\77\13\0\124\76"+ + "\4\0\2\77\2\0\2\77\2\0\132\76\1\0\3\77\6\0\50\76"+ + "\u1cd3\0\u51a6\76\u0c5a\0\u2ba4\76\134\0\u0800\0\u1ffe\0\2\0"; + + /** + * Translates characters to character classes + */ + final private static char [] yycmap = yy_unpack_cmap(yycmap_packed); + + /** + * Translates a state to a row index in the transition table + */ + final private static int yy_rowMap [] = { + 0, 71, 142, 213, 284, 355, 426, 497, 568, 639, + 710, 781, 852, 923, 994, 1065, 1136, 1207, 1278, 1349, + 1420, 1491, 1562, 1633, 1704, 1775, 1846, 1917, 1988, 2059, + 2130, 2201, 2272, 2343, 2414, 2485, 2556, 2627, 2698, 2769, + 2840, 2911, 2982, 3053, 3124, 3195, 3266, 3337, 3408, 3479, + 3550, 3621, 3692, 3763, 3834, 3905, 3976, 4047, 4118, 4189, + 4260, 4331, 4402, 4473, 4402, 4473, 4544, 4402, 4402, 4473, + 4615, 4686, 4757, 4828, 4899, 4970, 5041, 5112, 4402, 4473, + 5183, 5254, 5325, 4402, 5396, 5396, 5467, 5538, 5609, 5183, + 4402, 5680, 5751, 4402, 5822, 5893, 5964, 6035, 4402, 4473, + 6106, 6177, 6248, 6319, 6390, 6461, 4402, 6532, 6532, 6603, + 6674, 6745, 6816, 6887, 4402, 6958, 7029, 7100, 7171, 7242, + 7313, 4402, 7384, 7455, 7526, 7597, 7668, 7739, 7810, 7881, + 4402, 7952, 8023, 8094, 8165, 8236, 8307, 8378, 8449, 8449, + 8520, 8591, 8662, 8733, 8733, 8804, 8875, 8946, 9017, 9017, + 9088, 9159, 9230, 9301, 4402, 9372, 9372, 9443, 9514, 9585, + 9656, 4402, 4402, 4473, 4402, 4473, 9727, 9798, 9869, 9940, + 4402, 10011, 10082, 10153, 10224, 4402, 10295, 10366, 10437, 10508, + 4402, 4402, 10579, 4402, 10650, 10721, 10650, 10792, 10863, 10792, + 4402, 4402, 10934, 11005, 11076, 4402, 11147, 11218, 11289, 11360, + 11431, 4402, 4402, 11502, 4402, 11573, 11644, 11573, 11715, 11786, + 11715, 4402, 4402, 11857, 11928, 11999, 4402, 12070, 12141, 12212, + 4402, 4402, 12283, 12354, 12425, 12496, 12567, 4402, 12638, 12709, + 12780, 12851, 12922, 12993, 13064, 13135, 4402, 13206, 13277, 13348, + 4402, 4402, 5396, 5538, 4402, 13419, 5609, 13490, 5680, 5822, + 5893, 13561, 5964, 4402, 13632, 13703, 6035, 13774, 4402, 12354, + 4402, 6532, 6603, 4402, 13845, 6674, 13916, 4402, 13987, 14058, + 7384, 14129, 7597, 4402, 14200, 7668, 14271, 14342, 14413, 14484, + 14555, 14626, 8165, 4402, 14697, 14768, 8449, 8520, 4402, 14839, + 14910, 14981, 15052, 15123, 8662, 8449, 8733, 8804, 4402, 8875, + 8946, 8733, 9017, 9088, 4402, 15194, 15265, 15336, 15407, 15478, + 15549, 15620, 9372, 9443, 4402, 15691, 15762, 15833, 15904, 15975, + 16046, 16117, 16188, 16259, 4402, 4402, 4402, 16330, 4402, 4402, + 16401, 16472, 16543, 16614, 10650, 4402, 16685, 16756, 10792, 4402, + 16827, 16898, 16969, 17040, 17111, 17182, 17253, 17324, 17395, 11360, + 11573, 4402, 17466, 17537, 11715, 4402, 17608, 17679, 17750, 17821, + 17892, 17963, 18034, 18105, 18176, 4402, 4402, 4402, 18247, 18318, + 18389, 18460, 18531, 4402, 18602, 18673, 4402, 4402, 4402, 4402, + 4402, 4828, 18744, 18815, 18886, 18957, 19028, 19099, 19170, 19099, + 19241, 19312, 19241, 19383, 19454, 19525, 19596, 19667, 19738, 19809, + 19809, 19880, 19951, 19951, 20022, 9230, 9230, 9230, 20093, 20164, + 20235, 20235, 20306, 9585, 9585, 9585, 20377, 20448, 16543, 20519, + 10437, 10437, 10437, 20590, 20661, 10650, 10650, 10650, 20732, 20803, + 10792, 10792, 10792, 20874, 20945, 10934, 10934, 10934, 17111, 21016, + 21087, 11147, 11147, 11147, 17324, 21158, 21229, 11360, 11360, 11360, + 21300, 11573, 11573, 11573, 21371, 21442, 11715, 11715, 11715, 21513, + 21584, 11857, 11857, 11857, 17892, 21655, 21726, 12070, 12070, 12070, + 18105, 21797, 4402, 4402, 21868, 21939, 4402, 22010, 22081, 22152, + 22223, 7384, 4402, 4402, 22294, 22365, 22436, 22507, 22578, 15052, + 15407, 9230, 22649, 15904, 9585, 22720, 4402, 10437, 10650, 22791, + 10792, 22862, 10934, 22933, 4402, 11147, 23004, 11360, 11573, 23075, + 11715, 23146, 11857, 23217, 4402, 12070, 23288, 23359, 23430, 23501, + 23572, 23643, 23714, 23785, 23856, 23927, 23998, 24069, 24140, 24211, + 24282, 24353, 24424, 24495, 24566, 24637, 24708, 24779, 24850, 4828, + 24921, 24992, 25063, 25134, 25205, 4402, 4402, 25276, 25347, 25418, + 25489, 17111, 17324, 25560, 25631, 17892, 18105, 25702, 25773, 25844, + 25915, 4402, 4402, 4402, 25986, 26057, 26128, 26199, 26270, 26341, + 26412, 26483, 7100, 26554, 26625, 26696, 26767, 26838, 26909, 26980, + 4402, 27051, 27122, 9230, 9585, 10650, 10792, 11573, 11715, 27193, + 27264, 27335, 27406, 27477, 27548, 27619, 27690, 4828, 27761, 27832, + 27903, 27974, 28045, 28116, 28187, 28258, 28329, 28400, 28471, 28542, + 28613, 28684, 28755, 28826, 28897, 28968, 29039, 29110, 29181, 29252, + 29323, 29394, 29465, 29536, 29607, 29678, 29749, 29820, 29891, 29962, + 30033, 30104, 30175, 30246, 4402, 30317, 30388, 30459, 30530, 7100, + 30601, 30672, 30743, 30814, 30885, 30956, 31027, 31098, 31169, 31240, + 31311, 31382, 31453, 31524 + }; + + /** + * The packed transition table of the DFA + */ + final private static String yy_packed = + "\1\71\1\72\11\71\1\73\1\71\1\74\4\71\1\75"+ + "\42\71\1\76\21\71\1\77\1\100\105\77\1\101\1\102"+ + "\21\101\1\103\2\101\1\104\60\101\1\105\1\106\105\105"+ + "\1\101\1\102\5\101\1\107\16\101\1\104\61\101\1\102"+ + "\2\101\1\110\1\111\2\101\2\112\5\101\1\111\6\101"+ + "\1\111\1\113\1\114\4\112\1\101\10\112\1\115\2\112"+ + "\1\101\11\112\1\115\1\112\1\101\4\112\1\101\4\112"+ + "\1\101\4\112\2\101\1\112\1\101\1\102\2\101\1\110"+ + "\1\116\11\101\1\116\6\101\1\116\60\101\1\117\1\120"+ + "\2\117\1\121\21\117\1\104\60\117\1\101\1\102\2\101"+ + "\1\122\1\111\2\101\2\123\5\101\1\111\6\101\1\111"+ + "\6\123\1\101\13\123\1\101\13\123\1\101\4\123\1\101"+ + "\4\123\1\101\4\123\2\101\1\123\1\101\1\102\2\101"+ + "\1\122\1\111\2\101\2\123\5\101\1\111\6\101\1\111"+ + "\6\123\1\101\13\123\1\124\13\123\1\101\4\123\1\101"+ + "\4\123\1\101\4\123\2\101\1\123\1\125\1\102\1\101"+ + "\1\126\1\127\1\111\4\125\1\130\1\125\1\131\2\125"+ + "\1\111\6\125\1\111\60\125\1\101\1\102\2\101\1\132"+ + "\21\101\1\104\61\101\1\102\1\133\1\134\1\101\1\111"+ + "\2\101\2\135\5\101\1\111\6\101\1\111\6\135\1\101"+ + "\13\135\1\101\13\135\1\101\4\135\1\101\4\135\1\101"+ + "\4\135\2\101\1\135\1\101\1\102\1\133\1\134\1\101"+ + "\1\111\2\101\2\135\5\101\1\111\6\101\1\111\6\135"+ + "\1\101\13\135\1\136\13\135\1\101\4\135\1\101\4\135"+ + "\1\101\4\135\2\101\1\135\1\137\1\102\1\133\1\140"+ + "\1\137\1\111\4\137\1\141\1\137\1\142\2\137\1\111"+ + "\6\137\1\111\60\137\1\101\1\102\3\101\1\111\11\101"+ + "\1\111\6\101\1\111\60\101\1\143\1\144\20\143\1\145"+ + "\64\143\1\101\1\146\3\101\1\111\2\101\2\147\5\101"+ + "\1\111\2\101\1\150\3\101\1\111\6\147\1\101\13\147"+ + "\1\101\13\147\1\101\4\147\1\101\4\147\1\101\4\147"+ + "\2\101\1\147\1\101\1\146\3\101\1\151\11\101\1\151"+ + "\2\101\1\150\3\101\1\151\61\101\1\146\3\101\1\111"+ + "\2\101\2\152\5\101\1\111\2\101\1\150\3\101\1\111"+ + "\6\152\1\101\13\152\1\101\13\152\1\101\4\152\1\101"+ + "\4\152\1\101\4\152\2\101\1\152\1\101\1\146\3\101"+ + "\1\111\2\101\2\152\5\101\1\111\2\101\1\150\3\101"+ + "\1\111\6\152\1\101\13\152\1\153\13\152\1\101\4\152"+ + "\1\101\4\152\1\101\4\152\2\101\1\152\1\154\1\146"+ + "\1\101\1\155\1\154\1\111\4\154\1\156\1\154\1\157"+ + "\2\154\1\111\2\154\1\160\3\154\1\111\60\154\1\161"+ + "\1\162\1\163\1\164\4\161\2\165\15\161\6\166\1\161"+ + "\13\166\1\161\13\166\1\161\4\166\1\161\4\166\1\161"+ + "\1\167\3\166\2\161\1\166\1\101\1\170\1\163\1\164"+ + "\1\101\1\111\2\101\2\171\5\101\1\111\6\101\1\111"+ + "\6\171\1\101\13\171\1\101\13\171\1\101\4\171\1\101"+ + "\4\171\1\101\4\171\2\101\1\171\1\101\1\170\1\163"+ + "\1\164\1\101\1\111\2\101\2\171\5\101\1\111\6\101"+ + "\1\111\6\171\1\101\13\171\1\172\13\171\1\101\4\171"+ + "\1\101\4\171\1\101\4\171\2\101\1\171\1\173\1\174"+ + "\1\163\1\175\1\173\1\111\4\173\1\176\1\173\1\177"+ + "\1\200\1\173\1\111\6\173\1\111\36\173\1\201\21\173"+ + "\1\101\1\202\1\203\2\101\1\111\11\101\1\111\6\101"+ + "\1\111\10\101\1\204\1\205\2\101\1\206\11\101\1\206"+ + "\1\101\1\205\1\204\27\101\1\102\1\203\2\101\1\111"+ + "\11\101\1\111\6\101\1\111\6\101\1\207\52\101\1\102"+ + "\1\203\2\101\1\111\2\101\2\210\5\101\1\111\6\101"+ + "\1\111\6\210\1\207\13\210\1\101\13\210\1\101\4\210"+ + "\1\101\4\210\1\101\4\210\2\101\1\210\1\101\1\102"+ + "\1\203\2\101\1\111\11\101\1\111\6\101\1\111\6\101"+ + "\1\207\7\101\1\211\6\101\1\212\11\101\1\211\12\101"+ + "\1\212\5\101\1\213\1\102\1\203\1\214\1\213\1\111"+ + "\4\213\1\215\1\213\1\216\2\213\1\111\6\213\1\111"+ + "\6\213\1\217\51\213\1\220\1\102\1\203\1\221\1\220"+ + "\1\111\4\220\1\222\1\220\1\223\2\220\1\111\6\220"+ + "\1\111\6\220\1\224\51\220\1\225\1\102\1\203\1\226"+ + "\1\225\1\111\4\225\1\227\1\225\1\230\2\225\1\111"+ + "\6\225\1\111\60\225\1\231\1\232\1\233\104\231\1\234"+ + "\1\102\1\203\1\235\1\234\1\111\4\234\1\236\1\234"+ + "\1\237\2\234\1\111\6\234\1\111\60\234\1\240\1\241"+ + "\1\242\104\240\1\243\1\244\105\243\1\101\1\102\24\101"+ + "\1\104\60\101\1\245\1\246\105\245\1\101\1\102\5\101"+ + "\1\247\16\101\1\104\60\101\1\250\1\251\3\250\1\252"+ + "\6\250\1\253\1\254\1\250\1\252\6\250\1\252\36\250"+ + "\1\255\21\250\1\256\1\251\3\256\1\257\4\256\1\260"+ + "\2\256\1\261\1\256\1\257\6\256\1\257\36\256\1\262"+ + "\21\256\1\263\1\264\10\263\1\265\1\263\1\266\1\267"+ + "\67\263\1\270\1\263\1\271\1\272\12\271\1\101\11\271"+ + "\1\273\60\271\1\274\1\275\10\274\1\101\13\274\1\276"+ + "\60\274\1\101\1\102\12\101\1\277\11\101\1\104\61\101"+ + "\1\102\10\101\1\300\13\101\1\104\60\101\1\301\1\302"+ + "\10\301\1\260\71\301\1\303\1\304\1\301\1\305\1\306"+ + "\12\305\1\253\67\305\1\307\1\304\1\305\1\310\1\311"+ + "\10\310\1\312\1\310\1\313\50\310\1\314\17\310\1\315"+ + "\1\310\1\316\1\317\12\316\1\101\11\316\1\320\60\316"+ + "\1\321\1\322\10\321\1\101\13\321\1\323\60\321\1\101"+ + "\1\102\12\101\1\324\11\101\1\104\61\101\1\102\10\101"+ + "\1\325\13\101\1\104\60\101\1\326\1\327\10\326\1\260"+ + "\71\326\1\330\1\331\1\326\1\332\1\333\12\332\1\253"+ + "\67\332\1\334\1\331\1\332\1\71\1\0\11\71\1\0"+ + "\1\71\1\0\4\71\1\0\42\71\1\0\21\71\3\0"+ + "\1\335\1\336\15\0\1\337\2\0\1\340\66\0\1\341"+ + "\2\0\2\342\5\0\1\341\6\0\1\341\6\342\1\0"+ + "\13\342\1\0\13\342\1\343\4\342\1\0\4\342\1\0"+ + "\4\342\2\0\1\342\1\344\1\0\11\344\1\0\1\344"+ + "\1\345\1\346\3\344\1\0\64\344\5\0\1\341\2\0"+ + "\2\347\5\0\1\341\6\0\1\341\6\347\1\0\13\347"+ + "\1\0\13\347\1\0\4\347\1\0\4\347\1\0\4\347"+ + "\2\0\1\347\1\344\1\0\11\344\1\0\2\344\1\350"+ + "\3\344\1\0\42\344\1\351\21\344\131\0\1\337\2\0"+ + "\1\352\104\0\1\353\72\0\1\354\101\0\1\355\111\0"+ + "\1\111\11\0\1\111\6\0\1\111\66\0\4\112\6\0"+ + "\1\112\6\0\6\112\1\0\13\112\1\0\13\112\1\0"+ + "\4\112\1\0\11\112\2\0\1\112\6\0\4\112\6\0"+ + "\1\112\6\0\2\112\2\356\2\112\1\0\13\112\1\0"+ + "\13\112\1\0\4\112\1\0\11\112\2\0\1\112\6\0"+ + "\4\112\6\0\1\112\6\0\2\112\1\356\1\357\2\112"+ + "\1\0\13\112\1\0\13\112\1\0\4\112\1\0\11\112"+ + "\2\0\1\112\6\0\4\112\6\0\1\112\6\0\2\112"+ + "\2\360\2\112\1\0\13\112\1\0\13\112\1\0\4\112"+ + "\1\0\11\112\2\0\1\112\5\0\1\116\11\0\1\116"+ + "\6\0\1\116\62\0\1\361\106\0\1\362\112\0\4\123"+ + "\6\0\1\123\6\0\6\123\1\0\13\123\1\0\13\123"+ + "\1\0\4\123\1\0\11\123\2\0\1\123\1\125\2\0"+ + "\1\363\1\125\1\0\4\125\1\0\1\125\1\0\2\125"+ + "\1\0\6\125\1\0\61\125\1\0\1\362\1\363\1\125"+ + "\1\0\4\125\1\0\1\125\1\0\2\125\1\0\6\125"+ + "\1\0\60\125\1\364\1\0\10\364\1\365\2\364\1\366"+ + "\47\364\1\366\21\364\1\367\1\0\12\367\1\365\1\370"+ + "\47\367\1\370\21\367\2\0\1\133\1\371\111\0\4\135"+ + "\6\0\1\135\6\0\6\135\1\0\13\135\1\0\13\135"+ + "\1\0\4\135\1\0\11\135\2\0\1\135\1\137\2\0"+ + "\1\372\1\137\1\0\4\137\1\0\1\137\1\0\2\137"+ + "\1\0\6\137\1\0\61\137\1\0\1\133\1\373\1\137"+ + "\1\0\4\137\1\0\1\137\1\0\2\137\1\0\6\137"+ + "\1\0\60\137\1\141\1\0\1\374\1\375\1\141\1\374"+ + "\4\141\1\376\1\141\1\374\1\377\1\141\1\374\6\141"+ + "\1\374\36\141\1\377\21\141\1\142\1\0\1\u0100\1\u0101"+ + "\1\142\1\u0100\4\142\1\u0100\1\142\1\376\1\u0102\1\142"+ + "\1\u0100\6\142\1\u0100\36\142\1\u0102\21\142\2\0\1\u0103"+ + "\126\0\1\337\2\0\1\u0104\67\0\4\147\6\0\1\147"+ + "\6\0\6\147\1\0\13\147\1\0\13\147\1\0\4\147"+ + "\1\0\11\147\2\0\1\147\2\0\1\u0105\111\0\1\151"+ + "\11\0\1\151\6\0\1\151\66\0\4\152\6\0\1\152"+ + "\6\0\6\152\1\0\13\152\1\0\13\152\1\0\4\152"+ + "\1\0\11\152\2\0\1\152\1\154\2\0\1\u0106\1\154"+ + "\1\0\4\154\1\0\1\154\1\0\2\154\1\0\6\154"+ + "\1\0\60\154\1\u0107\1\0\10\u0107\1\u0108\2\u0107\1\u0109"+ + "\47\u0107\1\u0109\21\u0107\1\u010a\1\0\12\u010a\1\u0108\1\u010b"+ + "\47\u010a\1\u010b\21\u010a\1\154\1\0\1\u0105\1\u0106\1\154"+ + "\1\0\4\154\1\0\1\154\1\0\2\154\1\0\6\154"+ + "\1\0\60\154\1\161\3\0\23\161\6\0\1\161\13\0"+ + "\1\161\13\0\1\161\4\0\1\161\4\0\1\161\4\0"+ + "\2\161\4\0\1\335\16\0\1\337\2\0\1\340\63\0"+ + "\1\u010c\104\0\1\161\3\0\2\161\4\165\6\161\1\165"+ + "\6\161\6\166\1\161\13\166\1\161\13\166\1\161\4\166"+ + "\1\161\4\166\1\165\4\166\2\161\1\166\6\0\4\166"+ + "\6\0\1\166\6\0\6\166\1\0\13\166\1\0\13\166"+ + "\1\0\4\166\1\0\11\166\2\0\1\166\6\0\4\166"+ + "\6\0\1\166\6\0\6\166\1\0\7\166\1\u010d\3\166"+ + "\1\0\13\166\1\0\4\166\1\0\11\166\2\0\1\166"+ + "\3\0\1\335\4\0\2\u010e\10\0\1\337\2\0\1\340"+ + "\1\0\6\u010e\1\0\13\u010e\1\0\13\u010e\1\0\4\u010e"+ + "\1\0\4\u010e\1\0\4\u010e\2\0\1\u010e\6\0\4\171"+ + "\6\0\1\171\6\0\6\171\1\0\13\171\1\0\13\171"+ + "\1\0\4\171\1\0\11\171\2\0\1\171\1\173\2\0"+ + "\1\u010f\1\173\1\0\4\173\1\0\1\173\1\0\2\173"+ + "\1\0\6\173\1\0\60\173\3\0\1\335\4\0\2\u0110"+ + "\10\0\1\337\2\0\1\340\1\0\6\u0110\1\0\13\u0110"+ + "\1\0\13\u0110\1\0\4\u0110\1\0\4\u0110\1\0\4\u0110"+ + "\2\0\1\u0110\1\173\1\0\1\u010c\1\u010f\1\173\1\0"+ + "\4\173\1\0\1\173\1\0\2\173\1\0\6\173\1\0"+ + "\60\173\1\u0111\1\0\10\u0111\1\u0112\2\u0111\1\u0113\47\u0111"+ + "\1\u0113\21\u0111\1\u0114\1\0\12\u0114\1\u0112\1\u0115\47\u0114"+ + "\1\u0115\21\u0114\1\173\2\0\1\u010f\1\173\1\0\4\173"+ + "\1\0\1\173\1\0\1\173\1\u0116\1\0\6\173\1\0"+ + "\61\173\2\0\1\u010f\1\173\1\0\4\173\1\0\1\173"+ + "\1\0\1\173\1\u0117\1\0\6\173\1\0\60\173\3\0"+ + "\1\335\16\0\1\337\2\0\1\u0104\130\0\1\u0118\2\0"+ + "\1\u0118\75\0\1\u0119\14\0\1\u0119\63\0\2\u011a\52\0"+ + "\23\u011b\1\u011c\63\u011b\6\0\4\210\6\0\1\210\6\0"+ + "\6\210\1\0\13\210\1\0\13\210\1\0\4\210\1\0"+ + "\11\210\2\0\1\210\53\0\1\u011d\5\0\1\u011d\116\0"+ + "\1\u011e\10\0\1\u011e\4\0\1\213\2\0\1\u011f\1\213"+ + "\1\0\4\213\1\0\1\213\1\0\2\213\1\0\6\213"+ + "\1\0\60\213\1\u0120\1\0\10\u0120\1\u0121\2\u0120\1\u0122"+ + "\47\u0120\1\u0122\21\u0120\1\u0123\1\0\1\u0123\2\u0124\1\u0123"+ + "\4\u0124\2\u0123\1\u0125\1\u0126\1\u0123\4\u0124\1\u0123\11\u0124"+ + "\1\u0123\27\u0124\1\u0126\10\u0124\2\u0123\4\u0124\2\u0123\1\u0124"+ + "\1\217\2\u011b\1\u0127\1\217\1\u011b\4\217\1\u011b\1\217"+ + "\1\u011b\2\217\1\u011b\3\217\1\u0128\2\217\1\u011b\60\217"+ + "\1\220\2\0\1\u0129\1\220\1\0\4\220\1\0\1\220"+ + "\1\0\2\220\1\0\6\220\1\0\60\220\12\u012a\1\u012b"+ + "\74\u012a\14\u012c\1\u012b\72\u012c\1\224\2\u011b\1\u012d\1\224"+ + "\1\u011b\4\224\1\u011b\1\224\1\u011b\2\224\1\u011b\3\224"+ + "\1\u012e\2\224\1\u011b\60\224\1\225\2\0\1\u012f\1\225"+ + "\1\0\4\225\1\0\1\225\1\0\2\225\1\0\6\225"+ + "\1\0\60\225\1\u0130\1\0\10\u0130\1\u0131\2\u0130\1\u0132"+ + "\47\u0130\1\u0132\21\u0130\1\u0133\1\0\1\u0133\2\u0134\1\u0133"+ + "\4\u0134\2\u0133\1\u0135\1\u0136\1\u0133\4\u0134\1\u0133\11\u0134"+ + "\1\u0133\27\u0134\1\u0136\10\u0134\2\u0133\4\u0134\2\u0133\1\u0134"+ + "\2\231\1\0\106\231\1\0\17\231\1\u0137\2\231\1\u0138"+ + "\61\231\1\234\2\0\1\u0139\1\234\1\0\4\234\1\0"+ + "\1\234\1\0\2\234\1\0\6\234\1\0\60\234\1\u013a"+ + "\1\0\10\u013a\1\u013b\2\u013a\1\u013c\47\u013a\1\u013c\21\u013a"+ + "\1\u013d\1\0\1\u013d\2\u013e\1\u013d\4\u013e\2\u013d\1\u013f"+ + "\1\u0140\1\u013d\4\u013e\1\u013d\11\u013e\1\u013d\27\u013e\1\u0140"+ + "\10\u013e\2\u013d\4\u013e\2\u013d\1\u013e\2\240\1\0\106\240"+ + "\1\0\17\240\1\u0141\2\240\1\u0142\61\240\7\0\1\u0143"+ + "\77\0\1\250\1\0\12\250\1\0\1\u0144\47\250\1\u0144"+ + "\21\250\3\0\1\u0145\16\0\1\337\2\0\1\352\61\0"+ + "\1\250\1\0\3\250\1\252\6\250\1\0\1\u0144\1\250"+ + "\1\252\6\250\1\252\36\250\1\u0144\37\250\1\u0146\106\250"+ + "\1\u0147\70\250\1\256\1\0\10\256\1\0\2\256\1\u0148"+ + "\47\256\1\u0148\22\256\1\0\3\256\1\257\4\256\1\0"+ + "\2\256\1\u0148\1\256\1\257\6\256\1\257\36\256\1\u0148"+ + "\37\256\1\u0149\106\256\1\u014a\70\256\12\263\1\0\1\263"+ + "\1\0\1\u014b\67\263\1\0\13\263\1\0\1\263\1\0"+ + "\1\u014b\4\263\1\u014c\62\263\1\0\13\263\1\0\1\263"+ + "\1\0\1\263\1\u014d\66\263\1\u014e\1\263\14\u014f\1\u0150"+ + "\106\u014f\1\u0150\5\u014f\1\u0151\2\u014f\1\u0152\61\u014f\12\u0153"+ + "\1\u0154\106\u0153\1\u0154\7\u0153\1\u0155\2\u0153\1\u0156\61\u0153"+ + "\12\301\1\0\71\301\1\u0157\1\0\13\301\1\0\7\301"+ + "\1\u0158\61\301\1\u0157\1\0\13\301\1\u0159\74\301\14\305"+ + "\1\0\67\305\1\u015a\1\0\15\305\1\0\5\305\1\u015b"+ + "\61\305\1\u015a\1\0\15\305\1\u015c\72\305\12\310\1\0"+ + "\1\310\1\0\70\310\1\0\13\310\1\0\1\310\1\0"+ + "\5\310\1\u015d\62\310\1\0\13\310\1\0\1\310\1\0"+ + "\1\310\1\u015e\66\310\1\0\1\310\14\u015f\1\u0160\106\u015f"+ + "\1\u0160\5\u015f\1\u0161\2\u015f\1\u0162\61\u015f\12\u0163\1\u0164"+ + "\106\u0163\1\u0164\7\u0163\1\u0165\2\u0163\1\u0166\61\u0163\12\326"+ + "\1\0\71\326\1\u0167\1\0\13\326\1\0\7\326\1\u0168"+ + "\61\326\1\u0167\1\0\13\326\1\u0169\74\326\14\332\1\0"+ + "\67\332\1\u016a\1\0\15\332\1\0\5\332\1\u016b\61\332"+ + "\1\u016a\1\0\15\332\1\u016c\72\332\7\0\1\u016d\11\0"+ + "\1\u016e\3\0\1\u016f\23\0\1\u0170\44\0\1\u0171\25\0"+ + "\1\u0172\56\0\1\341\2\0\2\u0173\5\0\1\341\6\0"+ + "\1\341\6\u0173\1\0\13\u0173\1\0\13\u0173\1\0\4\u0173"+ + "\1\0\4\u0173\1\0\4\u0173\2\0\1\u0173\1\u0174\1\0"+ + "\3\u0174\1\u0175\4\342\1\u0174\1\0\3\u0174\1\u0175\1\342"+ + "\1\u0174\1\0\3\u0174\1\u0175\6\342\1\u0174\13\342\1\u0174"+ + "\13\342\1\u0174\4\342\1\u0176\11\342\2\u0174\1\342\20\0"+ + "\1\u0177\7\0\1\u0178\73\0\1\345\71\0\105\346\1\u0179"+ + "\1\346\1\u0174\1\0\3\u0174\1\u0175\4\347\1\u0174\1\0"+ + "\3\u0174\1\u0175\1\347\1\u0174\1\0\3\u0174\1\u0175\6\347"+ + "\1\u0174\13\347\1\u0174\13\347\1\u0174\4\347\1\u017a\11\347"+ + "\2\u0174\1\347\105\350\1\u017b\1\350\65\0\1\351\56\0"+ + "\1\u0172\53\0\1\u017c\106\0\1\u017d\112\0\4\112\6\0"+ + "\1\112\6\0\4\112\2\u017e\1\0\13\112\1\0\13\112"+ + "\1\0\4\112\1\0\11\112\2\0\1\112\6\0\4\112"+ + "\6\0\1\112\6\0\4\112\1\u017e\1\u017f\1\0\13\112"+ + "\1\0\13\112\1\0\4\112\1\0\11\112\2\0\1\112"+ + "\6\0\4\112\6\0\1\112\6\0\6\112\1\0\13\112"+ + "\1\0\2\112\1\u0180\10\112\1\0\4\112\1\0\6\112"+ + "\1\u0180\2\112\2\0\1\112\12\364\1\365\3\364\1\0"+ + "\70\364\14\367\1\365\1\367\1\0\70\367\1\374\1\0"+ + "\10\374\1\376\2\374\1\u0181\47\374\1\u0181\21\374\1\141"+ + "\2\374\1\375\1\141\1\374\4\141\1\376\1\141\1\374"+ + "\1\141\1\137\1\374\6\141\1\374\60\141\1\u0100\1\0"+ + "\12\u0100\1\376\1\u0182\47\u0100\1\u0182\21\u0100\1\142\2\u0100"+ + "\1\u0101\1\142\1\u0100\4\142\1\u0100\1\142\1\376\1\142"+ + "\1\137\1\u0100\6\142\1\u0100\60\142\12\u0107\1\u0108\3\u0107"+ + "\1\0\70\u0107\14\u010a\1\u0108\1\u010a\1\0\70\u010a\6\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\13\166\1\0"+ + "\13\166\1\0\4\166\1\0\6\166\1\u0183\2\166\2\0"+ + "\1\166\6\0\4\u010e\6\0\1\u010e\6\0\6\u010e\1\0"+ + "\13\u010e\1\0\13\u010e\1\0\4\u010e\1\0\11\u010e\2\0"+ + "\1\u010e\6\0\4\u0110\6\0\1\u0110\6\0\6\u0110\1\0"+ + "\13\u0110\1\0\13\u0110\1\0\4\u0110\1\0\11\u0110\2\0"+ + "\1\u0110\12\u0111\1\u0112\3\u0111\1\0\70\u0111\14\u0114\1\u0112"+ + "\1\u0114\1\0\70\u0114\1\u0184\2\u0185\1\u0186\1\u0184\1\u0185"+ + "\4\u0184\1\u0185\1\u0184\1\u0185\2\u0184\1\u0185\6\u0184\1\u0185"+ + "\56\u0184\1\173\1\u0184\1\u0187\2\u0188\1\u0189\1\u0187\1\u0188"+ + "\4\u0187\1\u0188\1\u0187\1\u0188\2\u0187\1\u0188\6\u0187\1\u0188"+ + "\56\u0187\1\173\1\u0187\36\0\1\u018a\35\0\1\u018a\53\0"+ + "\1\u018b\14\0\1\u018b\73\0\1\u018c\11\0\1\u018c\76\0"+ + "\1\u018d\20\0\1\u018d\113\0\1\u018e\7\0\1\u018e\3\0"+ + "\12\u0120\1\u0121\3\u0120\1\0\70\u0120\1\u0123\1\0\12\u0123"+ + "\1\u0121\1\u018f\47\u0123\1\u018f\22\u0123\1\0\12\u0123\1\u0190"+ + "\1\u018f\47\u0123\1\u018f\21\u0123\14\0\1\u0191\72\0\14\u0123"+ + "\1\u0190\1\u0123\1\0\70\u0123\12\u0130\1\u0131\3\u0130\1\0"+ + "\70\u0130\1\u0133\1\0\12\u0133\1\u0131\1\u0192\47\u0133\1\u0192"+ + "\22\u0133\1\0\12\u0133\1\u0193\1\u0192\47\u0133\1\u0192\21\u0133"+ + "\14\0\1\u0194\72\0\14\u0133\1\u0193\1\u0133\1\0\70\u0133"+ + "\2\231\1\0\4\231\1\u0195\11\231\1\u0196\3\231\1\u0197"+ + "\23\231\1\u0198\37\231\1\0\32\231\1\u0199\51\231\12\u013a"+ + "\1\u013b\3\u013a\1\0\70\u013a\1\u013d\1\0\12\u013d\1\u013b"+ + "\1\u019a\47\u013d\1\u019a\22\u013d\1\0\12\u013d\1\u019b\1\u019a"+ + "\47\u013d\1\u019a\21\u013d\14\0\1\u019c\72\0\14\u013d\1\u019b"+ + "\1\u013d\1\0\70\u013d\2\240\1\0\4\240\1\u019d\11\240"+ + "\1\u019e\3\240\1\u019f\23\240\1\u01a0\37\240\1\0\32\240"+ + "\1\u01a1\51\240\22\0\1\u01a2\64\0\16\250\1\0\70\250"+ + "\16\256\1\0\70\256\12\263\1\0\1\263\1\0\1\263"+ + "\1\u01a3\66\263\1\u014e\10\263\1\u01a4\2\263\1\0\1\263"+ + "\1\0\1\u014b\3\263\1\u01a5\3\263\1\u01a6\23\263\1\u01a7"+ + "\33\263\1\0\1\263\12\u01a3\1\0\1\u01a3\1\0\70\u01a3"+ + "\1\0\1\u01a3\12\u014e\1\0\1\u014e\1\0\1\u01a8\67\u014e"+ + "\1\0\1\u014e\7\u014f\1\u01a9\4\u014f\1\u0150\4\u014f\1\u01aa"+ + "\3\u014f\1\u01ab\23\u014f\1\u01ac\51\u014f\1\u0150\20\u014f\1\u01ad"+ + "\51\u014f\7\u0153\1\u01ae\2\u0153\1\u0154\6\u0153\1\u01af\3\u0153"+ + "\1\u01b0\23\u0153\1\u01b1\47\u0153\1\u0154\22\u0153\1\u01b2\51\u0153"+ + "\12\301\1\0\103\301\1\u01b3\2\301\1\0\6\301\1\u01b4"+ + "\3\301\1\u01b5\23\301\1\u01b6\32\301\1\u0157\1\0\1\301"+ + "\104\u01b7\1\u01b8\2\u01b7\14\305\1\0\101\305\1\u01b9\4\305"+ + "\1\0\4\305\1\u01ba\3\305\1\u01bb\23\305\1\u01bc\32\305"+ + "\1\u015a\1\0\1\305\104\u01bd\1\u01be\2\u01bd\7\310\1\u01bf"+ + "\2\310\1\0\1\310\1\0\4\310\1\u01c0\3\310\1\u01c1"+ + "\23\310\1\u01c2\33\310\1\0\1\310\7\u015f\1\u01c3\4\u015f"+ + "\1\u0160\4\u015f\1\u01c4\3\u015f\1\u01c5\23\u015f\1\u01c6\51\u015f"+ + "\1\u0160\20\u015f\1\u01c7\51\u015f\7\u0163\1\u01c8\2\u0163\1\u0164"+ + "\6\u0163\1\u01c9\3\u0163\1\u01ca\23\u0163\1\u01cb\47\u0163\1\u0164"+ + "\22\u0163\1\u01cc\51\u0163\12\326\1\0\103\326\1\u01cd\2\326"+ + "\1\0\6\326\1\u01ce\3\326\1\u01cf\23\326\1\u01d0\32\326"+ + "\1\u0167\1\0\1\326\104\u01d1\1\u01d2\2\u01d1\14\332\1\0"+ + "\101\332\1\u01d3\4\332\1\0\4\332\1\u01d4\3\332\1\u01d5"+ + "\23\332\1\u01d6\32\332\1\u016a\1\0\1\332\104\u01d7\1\u01d8"+ + "\2\u01d7\7\0\1\u01d9\106\0\1\u01da\135\0\1\u01db\50\0"+ + "\1\u0173\1\0\11\u0173\1\0\6\u0173\1\0\64\u0173\1\u0174"+ + "\1\0\11\u0174\1\0\6\u0174\1\0\47\u0174\1\0\15\u0174"+ + "\1\0\3\u0174\1\u0175\5\u0174\1\0\3\u0174\1\u0175\2\u0174"+ + "\1\0\3\u0174\1\u0175\43\u0174\1\u01dc\14\u0174\20\0\1\u0177"+ + "\51\0\1\u01dd\34\0\1\u01de\15\0\3\u01de\2\0\1\u01de"+ + "\11\0\1\u01de\1\0\2\u01de\7\0\1\u01de\2\0\2\u01de"+ + "\6\0\1\u01de\11\0\1\112\1\u01df\2\112\6\0\1\112"+ + "\6\0\6\112\1\0\13\112\1\0\13\112\1\0\4\112"+ + "\1\0\11\112\2\0\1\112\6\0\4\112\6\0\1\112"+ + "\6\0\6\112\1\0\11\112\1\u01e0\1\112\1\0\1\u01e0"+ + "\12\112\1\0\4\112\1\0\11\112\2\0\1\112\12\374"+ + "\1\376\3\374\1\0\70\374\14\u0100\1\376\1\u0100\1\0"+ + "\70\u0100\6\0\3\166\1\u01e1\6\0\1\166\6\0\6\166"+ + "\1\0\13\166\1\0\13\166\1\0\4\166\1\0\11\166"+ + "\2\0\1\166\1\u0184\2\u0185\1\u0186\1\u0184\1\u0185\4\u0184"+ + "\1\u0185\1\u0184\1\u0185\2\u0184\1\u0185\6\u0184\1\u0185\56\u0184"+ + "\1\u01e2\1\u0184\105\u0185\1\u01e3\1\u0185\1\u0187\2\u0188\1\u0189"+ + "\1\u0187\1\u0188\4\u0187\1\u0188\1\u0187\1\u0188\2\u0187\1\u0188"+ + "\6\u0187\1\u0188\56\u0187\1\u01e2\1\u0187\105\u0188\1\u01e4\1\u0188"+ + "\41\0\1\u01e5\14\0\1\u01e5\63\0\2\u01e6\103\0\2\u01e7"+ + "\115\0\1\u01e8\14\0\1\u01e8\63\0\2\u01e9\52\0\14\u0123"+ + "\1\u0121\1\u0123\1\0\70\u0123\3\0\2\u01ea\1\0\4\u01ea"+ + "\2\0\1\u0125\1\u01ea\1\0\4\u01ea\1\0\11\u01ea\1\0"+ + "\40\u01ea\2\0\4\u01ea\2\0\1\u01ea\14\u0133\1\u0131\1\u0133"+ + "\1\0\70\u0133\3\0\2\u01eb\1\0\4\u01eb\2\0\1\u0135"+ + "\1\u01eb\1\0\4\u01eb\1\0\11\u01eb\1\0\40\u01eb\2\0"+ + "\4\u01eb\2\0\1\u01eb\2\231\1\0\4\231\1\u01ec\101\231"+ + "\1\0\33\231\1\u01ed\50\231\14\u013d\1\u013b\1\u013d\1\0"+ + "\70\u013d\3\0\2\u01ee\1\0\4\u01ee\2\0\1\u013f\1\u01ee"+ + "\1\0\4\u01ee\1\0\11\u01ee\1\0\40\u01ee\2\0\4\u01ee"+ + "\2\0\1\u01ee\2\240\1\0\4\240\1\u01ef\101\240\1\0"+ + "\33\240\1\u01f0\50\240\2\0\1\u01f1\104\0\7\263\1\u01f2"+ + "\2\263\1\0\1\263\1\0\1\u014b\67\263\1\0\1\263"+ + "\12\u014e\1\0\1\u014e\1\0\1\u014e\1\0\70\u014e\7\u014f"+ + "\1\u01f3\4\u014f\1\u0150\106\u014f\1\u0150\21\u014f\1\u01f4\50\u014f"+ + "\7\u0153\1\u01f5\2\u0153\1\u0154\106\u0153\1\u0154\23\u0153\1\u01f6"+ + "\50\u0153\7\301\1\u01f7\2\301\1\0\71\301\1\u0157\1\0"+ + "\1\301\12\u01f8\1\u01f9\72\u01f8\1\0\1\u01f8\7\305\1\u01fa"+ + "\4\305\1\0\67\305\1\u015a\1\0\1\305\14\u01fb\1\u01f9"+ + "\70\u01fb\1\0\1\u01fb\7\310\1\u01fc\2\310\1\0\1\310"+ + "\1\0\70\310\1\0\1\310\7\u015f\1\u01fd\4\u015f\1\u0160"+ + "\106\u015f\1\u0160\21\u015f\1\u01fe\50\u015f\7\u0163\1\u01ff\2\u0163"+ + "\1\u0164\106\u0163\1\u0164\23\u0163\1\u0200\50\u0163\7\326\1\u0201"+ + "\2\326\1\0\71\326\1\u0167\1\0\1\326\12\u0202\1\u0203"+ + "\72\u0202\1\0\1\u0202\7\332\1\u0204\4\332\1\0\67\332"+ + "\1\u016a\1\0\1\332\14\u0205\1\u0203\70\u0205\1\0\1\u0205"+ + "\37\0\1\u0206\141\0\1\u01dc\34\0\1\u01de\15\0\3\u01de"+ + "\2\0\1\u01de\11\0\1\u01de\1\0\2\u01de\7\0\1\u01de"+ + "\1\0\1\u01dd\2\u01de\6\0\1\u01de\11\0\4\112\6\0"+ + "\1\112\6\0\6\112\1\0\7\112\1\u0207\3\112\1\0"+ + "\13\112\1\0\4\112\1\0\11\112\2\0\1\112\6\0"+ + "\4\112\6\0\1\112\6\0\6\112\1\0\6\112\1\u0208"+ + "\4\112\1\0\13\112\1\0\1\112\1\u0208\2\112\1\0"+ + "\11\112\2\0\1\112\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\6\166\1\u0209\4\166\1\0\6\166\1\u020a"+ + "\4\166\1\0\4\166\1\0\11\166\2\0\1\166\53\0"+ + "\1\u020b\5\0\1\u020b\73\0\1\u020c\14\0\1\u020c\66\0"+ + "\1\u020d\11\0\1\u020d\74\0\1\u020e\11\0\1\u020e\77\0"+ + "\1\u020f\14\0\1\u020f\23\0\2\231\1\0\34\231\1\u0210"+ + "\47\231\2\240\1\0\34\240\1\u0211\47\240\14\u014f\1\u0150"+ + "\22\u014f\1\u0212\47\u014f\12\u0153\1\u0154\24\u0153\1\u0213\47\u0153"+ + "\12\u01f8\1\u01b7\71\u01f8\1\u0214\1\u01b7\1\u01f8\14\u01fb\1\u01bd"+ + "\67\u01fb\1\u0215\1\u01bd\1\u01fb\14\u015f\1\u0160\22\u015f\1\u0216"+ + "\47\u015f\12\u0163\1\u0164\24\u0163\1\u0217\47\u0163\12\u0202\1\u01d1"+ + "\71\u0202\1\u0218\1\u01d1\1\u0202\14\u0205\1\u01d7\67\u0205\1\u0219"+ + "\1\u01d7\1\u0205\40\0\1\u021a\54\0\4\112\6\0\1\112"+ + "\6\0\6\112\1\0\13\112\1\0\4\112\1\u021b\6\112"+ + "\1\0\4\112\1\0\11\112\2\0\1\112\6\0\4\112"+ + "\6\0\1\112\6\0\6\112\1\0\3\112\1\u021c\7\112"+ + "\1\0\4\112\1\u021c\6\112\1\0\4\112\1\0\11\112"+ + "\2\0\1\112\6\0\4\166\6\0\1\166\6\0\6\166"+ + "\1\0\11\166\1\u021d\1\166\1\0\13\166\1\0\4\166"+ + "\1\0\11\166\2\0\1\166\6\0\4\166\6\0\1\166"+ + "\6\0\6\166\1\0\10\166\1\u021e\2\166\1\0\13\166"+ + "\1\0\4\166\1\0\11\166\2\0\1\166\54\0\1\u021f"+ + "\24\0\1\u021f\52\0\1\u0220\20\0\1\u0220\70\0\1\u0221"+ + "\13\0\1\u0221\53\0\2\u0222\112\0\1\u0223\35\0\1\u0223"+ + "\12\0\2\231\1\0\35\231\1\u0224\46\231\2\240\1\0"+ + "\35\240\1\u0225\46\240\14\u014f\1\u0150\23\u014f\1\u0226\46\u014f"+ + "\12\u0153\1\u0154\25\u0153\1\u0227\46\u0153\12\u01f8\1\u0228\71\u01f8"+ + "\1\u0214\1\u01b7\1\u01f8\14\u01fb\1\u0229\67\u01fb\1\u0215\1\u01bd"+ + "\1\u01fb\14\u015f\1\u0160\23\u015f\1\u022a\46\u015f\12\u0163\1\u0164"+ + "\25\u0163\1\u022b\46\u0163\12\u0202\1\u022c\71\u0202\1\u0218\1\u01d1"+ + "\1\u0202\14\u0205\1\u022d\67\u0205\1\u0219\1\u01d7\1\u0205\41\0"+ + "\1\u022e\53\0\4\112\6\0\1\112\6\0\6\112\1\0"+ + "\13\112\1\0\7\112\1\u022f\3\112\1\0\4\112\1\0"+ + "\11\112\2\0\1\112\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\11\166\1\u0230\1\166\1\0\13\166\1\0"+ + "\4\166\1\0\11\166\2\0\1\166\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\6\166\1\u0231\4\166\1\0"+ + "\13\166\1\0\4\166\1\0\11\166\2\0\1\166\43\0"+ + "\1\u0232\11\0\1\u0232\72\0\1\u0233\14\0\1\u0233\71\0"+ + "\1\u0234\14\0\1\u0234\30\0\2\231\1\0\36\231\1\u0235"+ + "\45\231\2\240\1\0\36\240\1\u0236\45\240\14\u014f\1\u0150"+ + "\24\u014f\1\u0237\45\u014f\12\u0153\1\u0154\26\u0153\1\u0238\45\u0153"+ + "\14\u015f\1\u0160\24\u015f\1\u0239\45\u015f\12\u0163\1\u0164\26\u0163"+ + "\1\u023a\45\u0163\40\0\1\u023b\54\0\4\112\6\0\1\112"+ + "\6\0\5\112\1\u023c\1\0\13\112\1\0\13\112\1\0"+ + "\4\112\1\0\11\112\2\0\1\112\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\13\166\1\0\4\166\1\u023d"+ + "\6\166\1\0\4\166\1\0\11\166\2\0\1\166\6\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\5\166\1\u023e"+ + "\5\166\1\0\13\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\2\231\1\0\35\231\1\u023f\46\231\2\240\1\0"+ + "\35\240\1\u0240\46\240\14\u014f\1\u0150\23\u014f\1\u0241\46\u014f"+ + "\12\u0153\1\u0154\25\u0153\1\u0242\46\u0153\14\u015f\1\u0160\23\u015f"+ + "\1\u0243\46\u015f\12\u0163\1\u0164\25\u0163\1\u0244\46\u0163\35\0"+ + "\1\u0245\57\0\4\112\6\0\1\112\6\0\6\112\1\0"+ + "\5\112\1\u0246\5\112\1\0\13\112\1\0\4\112\1\0"+ + "\11\112\2\0\1\112\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\13\166\1\0\13\166\1\0\4\166\1\0"+ + "\1\166\1\u0247\7\166\2\0\1\166\2\231\1\0\32\231"+ + "\1\u0248\51\231\2\240\1\0\32\240\1\u0249\51\240\14\u014f"+ + "\1\u0150\20\u014f\1\u024a\51\u014f\12\u0153\1\u0154\22\u0153\1\u024b"+ + "\51\u0153\14\u015f\1\u0160\20\u015f\1\u024c\51\u015f\12\u0163\1\u0164"+ + "\22\u0163\1\u024d\51\u0163\6\0\4\112\6\0\1\112\6\0"+ + "\6\112\1\0\7\112\1\u024e\3\112\1\0\13\112\1\0"+ + "\4\112\1\0\11\112\2\0\1\112\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\13\166\1\0\4\166\1\u024f"+ + "\6\166\1\0\4\166\1\0\11\166\2\0\1\166\6\0"+ + "\4\112\6\0\1\112\6\0\6\112\1\0\13\112\1\0"+ + "\13\112\1\0\4\112\1\0\11\112\2\0\1\u0250\6\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\10\166\1\u0251"+ + "\2\166\1\0\13\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\6\0\4\112\6\0\1\112\6\0\6\112\1\0"+ + "\5\112\1\u0252\5\112\1\0\13\112\1\0\4\112\1\0"+ + "\11\112\2\0\1\112\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\4\166\1\u0253\6\166\1\0\13\166\1\0"+ + "\4\166\1\0\11\166\2\0\1\166\6\0\4\112\6\0"+ + "\1\112\6\0\6\112\1\0\5\112\1\u0254\5\112\1\0"+ + "\13\112\1\0\4\112\1\0\11\112\2\0\1\112\6\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\5\166\1\u0255"+ + "\5\166\1\0\13\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\6\0\4\112\6\0\1\112\6\0\6\112\1\0"+ + "\13\112\1\0\4\112\1\u0256\6\112\1\0\4\112\1\0"+ + "\11\112\2\0\1\112\6\u0257\4\u0258\6\u0257\1\u0258\5\u0257"+ + "\1\0\6\u0258\1\u0257\13\u0258\1\u0257\13\u0258\1\u0257\4\u0258"+ + "\1\u0257\11\u0258\2\u0257\1\u0258\42\0\1\u0259\3\0\1\u025a"+ + "\7\0\1\u025b\1\u025c\21\0\1\u025d\13\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\4\166\1\u025e\3\166\1\u025f"+ + "\2\166\1\0\4\166\1\u0260\1\u0261\5\166\1\0\4\166"+ + "\1\0\6\166\1\u0262\2\166\2\0\1\166\57\0\1\u0263"+ + "\77\0\1\u0264\115\0\1\u0265\105\0\1\u0266\107\0\1\u0267"+ + "\35\0\4\166\6\0\1\166\6\0\6\166\1\0\13\166"+ + "\1\0\5\166\1\u0268\5\166\1\0\4\166\1\0\11\166"+ + "\2\0\1\166\6\0\4\166\6\0\1\166\6\0\6\166"+ + "\1\0\12\166\1\u0269\1\0\13\166\1\0\4\166\1\0"+ + "\11\166\2\0\1\166\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\13\166\1\0\5\166\1\u026a\5\166\1\0"+ + "\4\166\1\0\11\166\2\0\1\166\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\13\166\1\0\4\166\1\u026b"+ + "\6\166\1\0\4\166\1\0\11\166\2\0\1\166\6\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\13\166\1\0"+ + "\5\166\1\u026c\5\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\44\0\1\u026d\136\0\1\u026e\107\0\1\u026f\67\0"+ + "\1\u0270\125\0\1\u0271\17\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\6\166\1\u0272\4\166\1\0\13\166\1\0"+ + "\4\166\1\0\11\166\2\0\1\166\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\13\166\1\0\13\166\1\0"+ + "\4\166\1\0\1\166\1\u0273\7\166\2\0\1\166\6\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\13\166\1\0"+ + "\13\166\1\0\4\166\1\0\2\166\1\u0274\6\166\2\0"+ + "\1\166\6\0\4\166\6\0\1\166\6\0\6\166\1\0"+ + "\13\166\1\0\4\166\1\u0275\6\166\1\0\4\166\1\0"+ + "\11\166\2\0\1\166\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\13\166\1\0\13\166\1\0\4\166\1\0"+ + "\2\166\1\u0276\6\166\2\0\1\166\46\0\1\u0277\74\0"+ + "\1\u0278\106\0\1\u0279\116\0\1\u027a\105\0\1\u027b\51\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\10\166\1\u027c"+ + "\2\166\1\0\13\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\6\0\4\166\6\0\1\166\6\0\5\166\1\u027d"+ + "\1\0\13\166\1\0\13\166\1\0\4\166\1\0\11\166"+ + "\2\0\1\166\6\0\4\166\6\0\1\166\6\0\5\166"+ + "\1\u027e\1\0\13\166\1\0\13\166\1\0\4\166\1\0"+ + "\11\166\2\0\1\166\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\6\166\1\u027f\4\166\1\0\13\166\1\0"+ + "\4\166\1\0\11\166\2\0\1\166\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\5\166\1\u0280\5\166\1\0"+ + "\13\166\1\0\4\166\1\0\11\166\2\0\1\166\57\0"+ + "\1\u0281\131\0\1\u0282\52\0\1\u0283\106\0\1\u0284\46\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\13\166\1\0"+ + "\5\166\1\u0285\5\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\6\0\4\166\6\0\1\166\6\0\6\166\1\0"+ + "\13\166\1\0\13\166\1\0\4\166\1\0\7\166\1\u0286"+ + "\1\166\2\0\1\166\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\10\166\1\u0287\2\166\1\0\13\166\1\0"+ + "\4\166\1\0\11\166\2\0\1\166\6\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\10\166\1\u0288\2\166\1\0"+ + "\13\166\1\0\4\166\1\0\11\166\2\0\1\166\103\0"+ + "\1\u0289\63\0\1\u0271\131\0\1\u027b\106\0\1\u028a\11\0"+ + "\4\166\6\0\1\166\6\0\6\166\1\0\13\166\1\0"+ + "\13\166\1\0\4\166\1\0\10\166\1\u028b\2\0\1\166"+ + "\6\0\4\166\6\0\1\166\6\0\6\166\1\0\13\166"+ + "\1\0\6\166\1\u0276\4\166\1\0\4\166\1\0\11\166"+ + "\2\0\1\166\6\0\4\166\6\0\1\166\6\0\6\166"+ + "\1\0\13\166\1\0\13\166\1\0\4\166\1\0\10\166"+ + "\1\u0280\2\0\1\166\6\0\4\166\6\0\1\166\6\0"+ + "\6\166\1\0\13\166\1\0\13\166\1\0\4\166\1\0"+ + "\10\166\1\u028c\2\0\1\166\34\0\1\u0271\154\0\1\u028d"+ + "\12\0\4\166\6\0\1\166\6\0\5\166\1\u0276\1\0"+ + "\13\166\1\0\13\166\1\0\4\166\1\0\11\166\2\0"+ + "\1\166\6\0\4\166\6\0\1\166\6\0\6\166\1\0"+ + "\13\166\1\0\13\166\1\0\4\166\1\0\7\166\1\u028e"+ + "\1\166\2\0\1\166\56\0\1\u0271\36\0\4\166\6\0"+ + "\1\166\6\0\6\166\1\0\13\166\1\0\4\166\1\u0276"+ + "\6\166\1\0\4\166\1\0\11\166\2\0\1\166"; + + /** + * The transition table of the DFA + */ + final private static int yytrans [] = yy_unpack(yy_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + // final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", //$NON-NLS-1$ + "Internal error: unknown state", //$NON-NLS-1$ + "Error: could not match input", //$NON-NLS-1$ + "Error: pushback value was too large" //$NON-NLS-1$ + }; + + /** + * YY_ATTRIBUTE[aState] contains the attributes of state <code>aState</code> + */ + private final static byte YY_ATTRIBUTE[] = { + 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 9, 1, + 9, 1, 1, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 1, + 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, 1, 1, 9, 1, 1, + 1, 1, 9, 1, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, + 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, + 1, 9, 9, 1, 9, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 9, + 1, 1, 1, 1, 9, 9, 1, 9, 3, 3, 3, 3, 3, 3, 9, 9, + 1, 1, 1, 9, 1, 1, 1, 1, 1, 9, 9, 1, 9, 3, 3, 3, + 3, 3, 3, 9, 9, 1, 1, 1, 9, 1, 1, 1, 9, 9, 1, 1, + 0, 1, 0, 9, 1, 2, 1, 2, 1, 0, 0, 0, 9, 1, 1, 1, + 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 1, 0, + 0, 1, 9, 0, 9, 0, 0, 9, 0, 0, 0, 9, 1, 1, 0, 1, + 0, 9, 0, 0, 0, 1, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 9, 0, 0, 0, 1, 0, 0, 1, 0, 0, 9, 0, 0, 1, 0, 0, + 9, 0, 0, 0, 1, 0, 1, 1, 0, 0, 9, 0, 0, 0, 1, 0, + 1, 1, 0, 0, 9, 9, 9, 0, 9, 9, 1, 1, 1, 1, 2, 13, + 3, 2, 2, 13, 3, 2, 0, 1, 1, 0, 1, 1, 1, 1, 2, 13, + 3, 2, 2, 13, 3, 2, 0, 1, 1, 0, 1, 1, 0, 9, 9, 9, + 0, 0, 1, 1, 1, 9, 0, 0, 13, 9, 13, 9, 9, 1, 1, 1, + 0, 0, 1, 3, 2, 2, 3, 2, 2, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 2, 3, 3, 3, 2, 2, 3, 3, + 3, 2, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, + 1, 1, 2, 3, 3, 3, 2, 2, 3, 3, 3, 2, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 0, 9, 9, 0, 1, 9, 0, 1, 1, + 1, 5, 13, 13, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, + 9, 1, 3, 2, 3, 2, 1, 0, 9, 1, 0, 1, 3, 2, 3, 2, + 1, 0, 9, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 2, 2, 0, 0, 2, 2, 0, 0, 0, 1, 1, 1, 1, 0, 0, + 0, 9, 9, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 0, 1, 1, + 1, 9, 9, 9, 1, 1, 2, 2, 2, 2, 0, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 9, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, + 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 9, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1 + }; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + // private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + // private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + private int fTokenCount = 0; + + // required holders for white-space compacting + private boolean fShouldLoadBuffered = false; + private String fBufferedContext = null; + private int fBufferedStart = 1; + private int fBufferedLength = 0; + private ContextRegionContainer fBufferedEmbeddedContainer = null; + private String f_context = null; + + // state stack for handling embedded regions + private IntStack fStateStack = new IntStack(); + // a "hint" as to what an embedded region should be evaluated + private String fEmbeddedHint = UNDEFINED; + // a "hint" as to what state to enter once an embedded region has + // been completed + private int fEmbeddedPostState = YYINITIAL; + // the container used to create embedded regions + private ContextRegionContainer fEmbeddedContainer = null; + private static final String PROXY_CONTEXT = "PROXY_CONTEXT"; + + private String context = null; + private int start = 0; + private int textLength = 0; + private int length = 0; + + // offset for tracking position specific block tags + private int fOffset = 0; + + // the name of the current tag being opened + private String fCurrentTagName = null; + + // the name of the current tag inside of an embedded region + private String internalTagName = null; + private String internalContext = null; + + // the list of tag name BlockMarkers + private List fBlockMarkers = new ArrayList(0); + private List fNestablePrefixes = new ArrayList(1); + + // where the last internal container block was found + private int fLastInternalBlockStart = -1; + + // required to not seek text blocks on an end tag + private boolean fIsBlockingEnabled = false; + private boolean fIsCaseSensitiveBlocking = true; + + private static final boolean fForbidJSP = false; + + private int fELlevel = 0; + + private JSPParserRegionFactory fRegionFactory = new JSPParserRegionFactory(); + + /** + * user method + */ + public final void addBlockMarker(BlockMarker marker) { + if(containsTagName(marker.getTagName())) + return; + fBlockMarkers.add(marker); + } + /** + * user method + */ + public final void addNestablePrefix(TagMarker marker) { + fNestablePrefixes.add(marker); + } + /* user method */ + public List getNestablePrefixes() { + return fNestablePrefixes; + } + /** + * user method + */ + private boolean isNestable(String tagName) { + //Iterator blocks = fNestablePrefixes.iterator(); + //while(blocks.hasNext()) { + // TagMarker marker = (TagMarker)blocks.next(); + // String markerName = marker.getTagName(); + // if(tagName.length() > markerName.length() + 1 && tagName.startsWith(markerName) && tagName.charAt(markerName.length()) == ':') { + // return marker.isGlobal() || getOffset() >= marker.getMarker().getStart(); + // } + //} + //return false; + return true; + } + /** + * user method + */ + public final void removeNestablePrefix(String name) { + if (fNestablePrefixes != null) { + Iterator nestables = fNestablePrefixes.iterator(); + while (nestables.hasNext()) { + if (((TagMarker) nestables.next()).getTagName().equalsIgnoreCase(name)) + nestables.remove(); + } + } + } + /** + * user method + */ + public final void removeBlockMarker(BlockMarker marker) { + fBlockMarkers.remove(marker); + } + /** + * user method + */ + public final void removeBlockMarker(String tagname) { + if (fBlockMarkers != null) { + Iterator blocks = fBlockMarkers.iterator(); + while (blocks.hasNext()) { + if (((BlockMarker) blocks.next()).getTagName().equals(tagname)) + blocks.remove(); + } + } + } + /* user method */ + private final void assembleEmbeddedTagSequence(String startType, String endTagName) { + assembleEmbeddedContainer(startType, null, endTagName); + } + /* user method */ + private final void assembleEmbeddedContainer(String startType, String[] endTypes) { + assembleEmbeddedContainer(startType, endTypes, null); + } + /* user method */ + private final void assembleEmbeddedContainer(String startType, String endType) { + assembleEmbeddedContainer(startType, new String[]{endType}, null); + } + /** + * user method + * + * Assembles an embedded container beginning with the given startType as + * the first ContextRegion within it and of the type fEmbeddedHint. The + * endTypes[] array contains the context types that will cause a successful + * exit. Use of the endTagName parameter alters this behavior to force an + * exit on an XML_TAG_CLOSE after seeing an XML_TAG_NAME whose significant + * text matches the endTagName String. All contents in between are + * insignificant, and yes, this means comments are allowed inside. + **/ + private final void assembleEmbeddedContainer(String startType, String[] endTypes, String endTagName) { + // the context of the region being added to the embedded container + internalContext = startType; + // keep track of where this container began; to provide relative indeces for the regions + int containerStart = yychar; + boolean notFinished = true; + // keep track of where we seem to be so that the endTagName can be checked + boolean isInEndTag = false; + boolean isInFirstTag = true; + // create the embedded container and setup its "type" + if (fEmbeddedContainer == null) { + fEmbeddedContainer = new ContextRegionContainer(); + fEmbeddedContainer.setType(fEmbeddedHint); + fEmbeddedContainer.setStart(containerStart); + // TODO: parent region needs to be set .... but not sure where to get it from + // fEmbeddedContainer.setParent(parentRegion); + } + containerStart = fEmbeddedContainer.getStart(); + while (notFinished) { + // add the region to the container + if (internalContext != null && internalContext != PROXY_CONTEXT) { + ITextRegion newToken = fRegionFactory.createToken(internalContext, yychar - containerStart, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + fEmbeddedContainer.setLength(fEmbeddedContainer.getLength() + yylength()); + fEmbeddedContainer.setTextLength(fEmbeddedContainer.getTextLength() + yylength()); + // DW, 4/16/2003 token regions no longer have parents + //newToken.setParent(fEmbeddedContainer); + } + try { + // longscan determines whether to attempt a blockTagScan within the embedded container + boolean longscan = false; + // save the tokenizer state in case of a block tag scan + int previousState = yystate(); + String previousCurrentTagName = fCurrentTagName; + int previousPostState = fEmbeddedPostState; + String previousEmbeddedHint = fEmbeddedHint; + // determine if a block tag scan is necessary + if (internalContext == XML_TAG_NAME) { + internalTagName = yytext(); + if(!isNestable(internalTagName)) { + internalTagName = null; + // snagged a tag name we shouldn't have + fEmbeddedPostState = ST_ABORT_EMBEDDED; + notFinished = false; + } + } + else if (internalContext == XML_TAG_OPEN || internalContext == XML_END_TAG_OPEN) { + internalTagName = null; + } + // do upkeep for endTagName usage; must be here since the next token could be the close + if (internalContext == XML_END_TAG_OPEN) { + isInEndTag = true; + } else if (internalContext == XML_TAG_CLOSE) { + isInFirstTag = isInEndTag = false; + } else { + ITextRegionList embeddedRegions = fEmbeddedContainer.getRegions(); + if (embeddedRegions.size() > 2 && (embeddedRegions.get(embeddedRegions.size()-1)).getType() == XML_TAG_CLOSE && (embeddedRegions.get(embeddedRegions.size() - 3)).getType() == XML_TAG_OPEN && internalTagName != null) { + if (containsTagName(internalTagName)) { + longscan = true; + yybegin(ST_BLOCK_TAG_SCAN); + } + } + } + if (longscan) + fCurrentTagName = internalTagName; + // read the next region and context + internalContext = primGetNextToken(); + if (longscan) { + // Returning from a block tag scan requires restoring some state variables + // as well as handling the block region and setting up for normal scanning + // inside the embedded container + ITextRegion newToken = fRegionFactory.createToken(internalContext, yychar - containerStart, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + fEmbeddedContainer.setLength(fEmbeddedContainer.getLength() + yylength()); + fEmbeddedContainer.setTextLength(fEmbeddedContainer.getTextLength() + yylength()); + // DW, 4/16/2003 token regions no longer have parents + // newToken.setParent(fEmbeddedContainer); + longscan = false; + fEmbeddedPostState = previousPostState; + fEmbeddedHint = previousEmbeddedHint; + fCurrentTagName = previousCurrentTagName; + yybegin(previousState); + internalContext = primGetNextToken(); + } + } catch (IOException e) { + // primGetNextToken() calls may throw an IOException + // catch and do nothing since the isEOF check below + // will properly exit if the input was too short + } catch (Exception f) { + // some other exception happened; never should + Logger.logException(f); + } + boolean isEndingType = yystate() == ST_ABORT_EMBEDDED; + if(!isEndingType) { + // check for ending context + if (endTagName == null) { + for (int i = 0; i < endTypes.length; i++) { + isEndingType = isEndingType || (internalContext == endTypes[i]); + } + } + else { + isEndingType = ((isInEndTag && internalContext == XML_TAG_CLOSE) || (isInFirstTag && internalContext == XML_EMPTY_TAG_CLOSE)) && internalTagName != null && internalTagName.equals(endTagName); + } + } + ITextRegionList embeddedList = fEmbeddedContainer.getRegions(); + notFinished = notFinished && ((!isEndingType) && !isEOF() && (endTagName != null || internalContext != UNDEFINED) && !(internalContext == PROXY_CONTEXT && (embeddedList.get(embeddedList.size()-1)).getType() == UNDEFINED)); + } + // finish adding the last context + if (internalContext != null && internalContext != PROXY_CONTEXT) { + ITextRegion newToken = fRegionFactory.createToken(internalContext, yychar - containerStart, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + // DW, 4/16/2003 token regions no longer have parents + //newToken.setParent(fEmbeddedContainer); + fEmbeddedContainer.setLength(yychar - containerStart + yylength()); + fEmbeddedContainer.setTextLength(yychar - containerStart + yylength()); + } + yybegin(fEmbeddedPostState); + } + /* user method */ + public final boolean isCaseSensitiveBlocking() { + return fIsCaseSensitiveBlocking; + } + /* user method */ + public final void setCaseSensitiveBlocking(boolean newValue) { + fIsCaseSensitiveBlocking = newValue; + } + /* user method */ + public boolean getBlockMarkerAllowsJSP() { + return getBlockMarkerAllowsJSP(fCurrentTagName); + } + /* user method */ + public boolean getBlockMarkerAllowsJSP(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if(casesensitive && marker.getTagName().equals(name)) + return marker.allowsJSP(); + else if(!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return marker.allowsJSP(); + } + return true; + } + /* user method */ + public boolean getBlockMarkerCaseSensitivity() { + return getBlockMarkerCaseSensitivity(fCurrentTagName); + } + public boolean getBlockMarkerCaseSensitivity(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if(casesensitive && marker.getTagName().equals(name)) + return casesensitive; + else if(!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return casesensitive; + } + return true; + } + /* user method */ + public String getBlockMarkerContext() { + return getBlockMarkerContext(fCurrentTagName); + } + /* user method */ + public String getBlockMarkerContext(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + if(marker.getTagName().equals(name)) + return marker.getContext(); + } + return BLOCK_TEXT; + } + /* user method */ + public List getBlockMarkers() { + return fBlockMarkers; + } + /* user method */ + public final int getOffset() { + return fOffset + yychar; + } + private final boolean isBlockMarker() { + return isBlockMarker(fCurrentTagName); + } + private final boolean isBlockMarker(String tagName) { + if (!fIsBlockingEnabled) + return false; + return containsTagName(tagName); + } + /** + * user method + */ + public final void beginBlockTagScan(String newTagName) { + beginBlockMarkerScan(newTagName, BLOCK_TEXT); + } + /** + * user method + * + * Special tokenizer setup. Allows tokenization to be initiated at the + * start of a text block within a "newTagName" tag. + * + * Example: + * Tokenizer toker = new Tokenizer(); + * toker.setCaseSensitiveBlocking(false); + * toker.reset(new java.io.StringReader("afiuhqwkejhtasihgalkwhtq</scripter></scr></script>asgdasga")); + * toker.beginBlockMarkerScan("script", BLOCK_TEXT); + * toker.getRegions(); + * + * Returns: + * BLOCK_TEXT: 0-40 + * XML_END_TAG_OPEN: 41-42 + * XML_TAG_NAME: 43-48 + * XML_TAG_CLOSE: 49-49 + * XML_CONTENT: 50-57 + * + */ + public final void beginBlockMarkerScan(String newTagName, String blockcontext) { + yybegin(ST_BLOCK_TAG_SCAN); + fCurrentTagName = newTagName; + } + +/** + * Method doScan. + * + * Returns a context region for all of the text from the current position upto the end of input or + * to right *before* the first occurence of searchString + * + * @param searchString - target string to search for ex.: "-->", "</tagname" + * @param requireTailSeparator - whether the target must be immediately followed by whitespace or '>' + * @param allowJSP - check for and allow for JSP markup <%%> + * @param context - the context of the scanned region if non-zero length + * @param exitState - the state to go to if the region was of non-zero length + * @param abortState - the state to go to if the searchString was found immediately + * @return String - the context found: the desired context on a non-zero length match, the abortContext on immediate success + * @throws IOException + */ +private final String doScan(String searchString, boolean requireTailSeparator, boolean allowJSP, boolean allowCDATA, String searchContext, int exitState, int immediateFallbackState) throws IOException { + boolean stillSearching = true; + // Disable further block (probably) + fIsBlockingEnabled = false; + int searchStringLength = searchString.length(); + int n = 0; + char lastCheckChar; + int i; + boolean same = false; + // Check for JSP starts ("<%") if the tag is global like SCRIPT or STYLE + boolean checkJSPs = allowJSP && !fForbidJSP; + boolean checkedForJSPsOnce = !checkJSPs; + boolean checkedJSPsAtStartOnce = false; + + while (stillSearching) { + n = 0; + // Ensure that enough data from the input exists to compare against the search String. + n = yy_advance(); + while(n != YYEOF && yy_currentPos < searchStringLength) + n = yy_advance(); +// c = (char) n; + // If the input was too short or we've exhausted the input, stop immediately. + if (n == YYEOF && checkedForJSPsOnce) { + stillSearching = false; + } + else { + /** + * Look for starting JSPs "<%" + */ + checkedForJSPsOnce = true; + // 1) yy_currentPos - searchStringLength : There's at least searchStringLength of input available; once that's read, check for JSPs + // --- + // Look for a JSP beginning at current-searchStringLength; if so, backup and switch scanner states to handle it. + // Ensure that we've not encountered a complete block (<%%>) that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first scan would have come out this far anyway]. + if(checkJSPs && yy_currentPos > searchStringLength && yy_currentPos - searchStringLength != fLastInternalBlockStart && + yy_buffer[yy_currentPos - searchStringLength] == '<' && yy_buffer[yy_currentPos - searchStringLength + 1] == '%') { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + // 2) yy_currentPos - jspstarter.length : There's not searchStringLength of input available; check for a JSP 2 spots back in what we could read + // --- + // Look for a JSP beginning at the current position; this case wouldn't be handled by the preceding section + // since it relies upon *having* closeTagStringLength amount of input to work as designed. Must be sure we don't + // spill over the end of the buffer while checking. + else if(checkJSPs && yy_startRead != fLastInternalBlockStart && yy_currentPos > 0 && yy_currentPos < yy_buffer.length - 1 && + yy_buffer[yy_currentPos - 1] == '<' && yy_buffer[yy_currentPos] == '%') { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - 1; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + // 3) yy_currentPos..(yy_currentPos+jspStartlength-1) : Check at the start of the block one time + // --- + // Look for a JSP beginning immediately in the block area; this case wouldn't be handled by the preceding section + // since it relies upon yy_currentPos equaling exactly the previous end +1 to work as designed. + else if(checkJSPs && !checkedJSPsAtStartOnce && yy_startRead != fLastInternalBlockStart && yy_startRead > 0 && + yy_startRead < yy_buffer.length - 1 && yy_buffer[yy_startRead] == '<' && yy_buffer[yy_startRead + 1] == '%') { + checkedJSPsAtStartOnce = true; + fLastInternalBlockStart = yy_markedPos = yy_startRead; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + + + /** + * Look for starting CDATA "<![CDATA[" + */ + // 1) yy_currentPos - searchStringLength: There's at least searchStringLength of input available; once that's read, check for CDATA + // --- + // Look for a CDATA beginning at current-searchStringLength; if so, backup and switch scanner states to handle it. + // Ensure that we've not encountered a complete block (<[!CDATA[]]>) that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first scan would have come out this far anyway]. +/* if(checkCDATA && yy_currentPos > searchStringLength && yy_currentPos + searchStringLength < yy_buffer.length && yy_currentPos - searchStringLength != fLastInternalBlockStart && + charsMatch(cdataStarter, yy_buffer, 0, yy_currentPos - searchStringLength)) { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + // go to a state where CDATA can be found + if (fEmbeddedContainer == null) { + fEmbeddedContainer = new ContextRegionContainer(); + fEmbeddedContainer.setType(searchContext); + fEmbeddedContainer.setStart(yychar); + } + ITextRegion newToken = fRegionFactory.createToken(searchContext, yychar, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + fEmbeddedContainer.setLength(fEmbeddedContainer.getLength() + yylength()); + fEmbeddedContainer.setTextLength(fEmbeddedContainer.getTextLength() + yylength()); + yybegin(YYINITIAL); + String context = primGetNextToken(); + if(context.equals(XMLRegionContexts.XML_CDATA_OPEN)) { + assembleEmbeddedContainer(XMLRegionContexts.XML_CDATA_OPEN, XMLRegionContexts.XML_CDATA_CLOSE); + } + yybegin(resumeState); + return searchContext; + } +*//* + // 2) yy_currentPos - cdataStarter.length: There's not searchStringLength of input available; check for a CDATA right here spots back in what we could read + // --- + // Look for a JSP beginning at the current position; this case wouldn't be handled by the preceding section + // since it relies upon *having* closeTagStringLength amount of input to work as designed. Must be sure we don't + // spill over the end of the buffer while checking. + else if(checkCDATA && yy_startRead != fLastInternalBlockStart && yy_currentPos > 0 && yy_currentPos < yy_buffer.length - 1 && + yy_buffer[yy_currentPos - 1] == '<' && yy_buffer[yy_currentPos] == '%') { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - 1; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + // 3) yy_currentPos : Check at the start of the block one time + // --- + // Look for a JSP beginning immediately in the block area; this case wouldn't be handled by the preceding section + // since it relies upon yy_currentPos equaling exactly the previous end +1 to work as designed. + else if(checkCDATA && !checkedForCDATAOnce && yy_startRead != fLastInternalBlockStart && yy_startRead > 0 && + yy_startRead < yy_buffer.length - 1 && yy_buffer[yy_startRead] == '<' && yy_buffer[yy_startRead + 1] == '%') { + checkedForCDATAOnce = true; + fLastInternalBlockStart = yy_markedPos = yy_startRead; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } +*/ + // Check the characters in the target versus the last targetLength characters read from the buffer + // and see if it matches + if (n == YYEOF) { + stillSearching = false; + } + else { + same = true; + // safety check for array accesses + if(yy_currentPos >= searchStringLength && yy_currentPos <= yy_buffer.length) { + for(i = 0; i < searchStringLength; i++) { + if(same && fIsCaseSensitiveBlocking) + same = yy_buffer[i + yy_currentPos - searchStringLength] == searchString.charAt(i); + else if(same && !fIsCaseSensitiveBlocking) + same = Character.toLowerCase(yy_buffer[i + yy_currentPos - searchStringLength]) == Character.toLowerCase(searchString.charAt(i)); + } + } + // safety check failed; no match is possible right now + else { + same = false; + } + } + if (same && requireTailSeparator && yy_currentPos < yy_buffer.length) { + // Additional check for close tags to ensure that targetString="</script" doesn't match + // "</scriptS" + lastCheckChar = yy_buffer[yy_currentPos]; + // Succeed on "</script>" and "</script " + if(lastCheckChar == '>' || Character.isWhitespace(lastCheckChar)) + stillSearching = false; + } + else { + stillSearching = !same || (yy_currentPos < yy_startRead + searchStringLength); + } + } + } + if (n != YYEOF || same) { + // We've stopped short of the end or definitely found a match + yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + // If the searchString occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if (yy_markedPos == yy_startRead) { + yybegin(immediateFallbackState); + return primGetNextToken(); + } + } + else { + // We ran through the rest of the input + yy_markedPos = yy_currentPos; + yy_currentPos++; + } + yybegin(exitState); + // If the ending occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if(yy_markedPos == yy_startRead) + return primGetNextToken(); + return searchContext; +} +/** + * user method + * does a lookahead for the current tag name + */ +private final String doBlockTagScan() throws IOException { + fIsCaseSensitiveBlocking = getBlockMarkerCaseSensitivity(); + return doScan("</" + fCurrentTagName, true, getBlockMarkerAllowsJSP(), true, getBlockMarkerContext(fCurrentTagName), YYINITIAL, YYINITIAL); +} + /** + * user method + * + * Converts the raw context String returned by the primGetNextToken() + * method into a full ITextRegion by pulling in values for the + * current offset within the scanning text. + * + * Returns null when EOF is encountered and attaches intermittently + * discovered whitespace onto the end of useful regions. + * + * Note that this algorithm caches the token following the one being returned + * so that whitespace can be collapsed. + */ + public final ITextRegion getNextToken() throws IOException { + fEmbeddedContainer = null; + // load the starting non-whitespace token (assume that it is so) + if (fShouldLoadBuffered) { + if (fBufferedEmbeddedContainer != null) { + ITextRegion container = fBufferedEmbeddedContainer; + fBufferedEmbeddedContainer = null; + fShouldLoadBuffered = false; + return container; + } + context = fBufferedContext; + start = fBufferedStart; + textLength = length = fBufferedLength; + fShouldLoadBuffered = false; + } else { + context = primGetNextToken(); + if (context == PROXY_CONTEXT) { + return fEmbeddedContainer; + } else if (context == XML_TAG_NAME || f_context == JSP_ROOT_TAG_NAME || f_context == JSP_DIRECTIVE_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } else if (context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } else if (context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + start = yychar; + textLength = length = yylength(); + if (yy_atEOF) { + fTokenCount++; + return null; + } + } + // store the next token + f_context = primGetNextToken(); + if (f_context == PROXY_CONTEXT) { + fBufferedEmbeddedContainer = fEmbeddedContainer; + fShouldLoadBuffered = true; + } else if (f_context == XML_TAG_NAME || f_context == JSP_ROOT_TAG_NAME || f_context == JSP_DIRECTIVE_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } else if (f_context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } else if (f_context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + fBufferedContext = f_context; + fBufferedStart = yychar; + fBufferedLength = yylength(); + fShouldLoadBuffered = true; + if (fBufferedContext == WHITE_SPACE) { + fShouldLoadBuffered = false; + length += fBufferedLength; + } + if (context == null) { + // EOF + if (Debug.debugTokenizer) { + System.out.println(getClass().getName() + " discovered " + fTokenCount + " tokens."); //$NON-NLS-2$//$NON-NLS-1$ + } + return null; + } + fTokenCount++; + return fRegionFactory.createToken(context, start, textLength, length, null, fCurrentTagName); + } + /* user method */ + public JSPTokenizer(){ + super(); + } + /* user method */ + public JSPTokenizer(char[] charArray){ + this(new CharArrayReader(charArray)); + } + /* user method */ + public void reset(char[] charArray) { + reset(new CharArrayReader(charArray), 0); + } + /* user method */ + public void reset(char[] charArray, int newOffset) { + reset(new CharArrayReader(charArray), newOffset); + } + /* user method */ + public void reset(java.io.InputStream in) { + reset(new java.io.InputStreamReader(in), 0); + } + /* user method */ + public void reset(java.io.InputStream in, int newOffset) { + reset(new java.io.InputStreamReader(in), newOffset); + } + /* user method */ + public void reset(java.io.Reader in) { + reset(in, 0); + } + /** + * user method * + * + * Reset internal counters and vars to "newly created" values, in the hopes + * that resetting a pre-existing tokenizer is faster than creating a new one. + * + * This method contains code blocks that were essentially duplicated from the + * <em>generated</em> output of this specification before this method was + * added. Those code blocks were under the above copyright. + */ + public void reset(java.io.Reader in, int newOffset) { + if (Debug.debugTokenizer) { + System.out.println("resetting tokenizer");//$NON-NLS-1$ + } + fOffset = newOffset; + + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + /* user vars: */ + fTokenCount = 0; + + fShouldLoadBuffered = false; + fBufferedContext = null; + fBufferedStart = 1; + fBufferedLength = 0; + fStateStack = new IntStack(); + + fLastInternalBlockStart = -1; + + context = null; + start = 0; + textLength = 0; + length = 0; + + fEmbeddedContainer = null; + + fELlevel = 0; + } + /** + * user method + * + */ + public BlockTokenizer newInstance() { + JSPTokenizer newInstance = new JSPTokenizer(); + // global tagmarkers can be shared; they have no state and + // are never destroyed (e.g. 'release') + for(int i = 0; i < fBlockMarkers.size(); i++) { + BlockMarker blockMarker = (BlockMarker) fBlockMarkers.get(i); + if(blockMarker.isGlobal()) + newInstance.addBlockMarker(blockMarker); + } + for(int i = 0; i < fNestablePrefixes.size(); i++) { + TagMarker marker = (TagMarker) fNestablePrefixes.get(i); + if(marker.isGlobal()) + newInstance.addNestablePrefix(marker); + } + return newInstance; + } + /* user method */ + private final String scanXMLCommentText() throws IOException { + // Scan for '-->' and return the text up to that point as + // XML_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_XML_COMMENT_END state and return the next + // context as usual. + return doScan("-->", false, true, true, XML_COMMENT_TEXT, ST_XML_COMMENT_END, ST_XML_COMMENT_END); + } + /* user method */ + private final String scanJSPCommentText() throws IOException { + // Scan for '--%>' and return the text up to that point as + // JSP_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_JSP_COMMENT_END state and return the next + // context as usual. + return doScan("--%>", false, false, true, JSP_COMMENT_TEXT, ST_JSP_COMMENT_END, ST_JSP_COMMENT_END); + } + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + public JSPTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public JSPTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed DFA transition table. + * + * @param packed the packed transition table + * @return the unpacked transition table + */ + private static int [] yy_unpack(String packed) { + int [] trans = new int[31595]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 8206) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do trans[j++] = value; while (--count > 0); + } + return trans; + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] yy_unpack_cmap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 1376) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning - from the SED JFlex skeleton + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + Logger.log(Logger.ERROR, YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + Logger.log(Logger.ERROR, YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + // DO NOT EXIT the VM on an error + // System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + /** + * user method - skeleton.sed + */ + protected final boolean containsTagName(char[] markerTagName, int offset, int tagnameLength) { + for(int j = 0; j < fBlockMarkers.size(); j++) { + BlockMarker marker = (BlockMarker)fBlockMarkers.get(j); + if(marker.getTagName().length() == tagnameLength) { + boolean matchesSoFar = true; + for(int i = 0; i < tagnameLength && matchesSoFar; i++) { + if(marker.isCaseSensitive()) { + if(marker.getTagName().charAt(i) != markerTagName[i + offset]) + matchesSoFar = false; + } + else { + if(Character.toLowerCase(marker.getTagName().charAt(i)) != Character.toLowerCase(markerTagName[i + offset])) + matchesSoFar = false; + } + } + if(matchesSoFar) + return true; + } + } + return false; + } + + /** + * user method - skeleton.sed + * + * Return ALL of the regions scannable within the remaining text + * Note: for verification use + */ + public final List getRegions() { + List tokens = new ArrayList(); + ITextRegion region = null; + try { + region = getNextToken(); + while(region != null) { + if (region != null) { + tokens.add(region); + } + region = getNextToken(); + } + } + catch (StackOverflowError e) { + Logger.logException(getClass().getName()+": input could not be tokenized correctly at position " + getOffset(), e);//$NON-NLS-1$ + throw e; + } + catch (Exception e) { + // Since this is convenience method and NOT the recommended + // way of getting tokens, many errors are simply hidden + Logger.logException("Exception not handled retrieving regions: " + e.getLocalizedMessage(), e);//$NON-NLS-1$ + } + return tokens; + } + /** + * user method - skeleton.sed + */ + private final void dump(String s) { + if (Debug.debugTokenizer) { + System.out.println(s + " (" + yychar + "-" + //$NON-NLS-2$//$NON-NLS-1$ + (yylength() + yychar) + "):\'" +//$NON-NLS-1$ + StringUtils.escape(yytext()) + "\'");//$NON-NLS-1$ + } + } + /* user method - skeleton.sed */ + public final boolean isEOF() { + return yy_atEOF; + } +/* user method - skeleton.sed */ +protected final boolean containsTagName(String markerTagName) { + Iterator blocks = fBlockMarkers.iterator(); + while(blocks.hasNext()) { + BlockMarker marker = (BlockMarker)blocks.next(); + if(marker.isCaseSensitive()) { + if(marker.getTagName().equals(markerTagName)) + return true; + } + else { + if(marker.getTagName().equalsIgnoreCase(markerTagName)) + return true; + } + } + return false; +} + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + // do nothing, this is the downstream parser's job + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + yy_pushbackPos = -1; + boolean yy_was_pushback; + + while (true) { + + yychar+= yylength(); + + boolean yy_counted = false; + for (yy_currentPos = yy_startRead; yy_currentPos < yy_markedPos; + yy_currentPos++) { + switch (yy_buffer[yy_currentPos]) { + case '\r': + yyline++; + yy_counted = true; + break; + case '\n': + if (yy_counted) + yy_counted = false; + else { + yyline++; + } + break; + default: + yy_counted = false; + } + } + + if (yy_counted) { + if ( yy_advance() == '\n' ) yyline--; + if ( !yy_atEOF ) yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + yy_state = yy_lexical_state; + + yy_was_pushback = false; + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + + int yy_next = yytrans[ yy_rowMap[yy_state] + yycmap[yy_input] ]; + if (yy_next == -1) break yy_forAction; + yy_state = yy_next; + + int yy_attributes = YY_ATTRIBUTE[yy_state]; + if ( (yy_attributes & 2) > 0 ) + yy_pushbackPos = yy_currentPos; + + if ( (yy_attributes & 1) > 0 ) { + yy_was_pushback = (yy_attributes & 4) > 0; + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if ( (yy_attributes & 8) > 0 ) break yy_forAction; + } + + } + } + + if (yy_was_pushback) + yy_markedPos = yy_pushbackPos; + + switch (yy_action) { + + case 622: + case 627: + case 634: + case 639: + { + if(Debug.debugTokenizer) + dump("jsp directive tag name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return JSP_DIRECTIVE_NAME; + } + case 655: break; + case 597: + { + if(Debug.debugTokenizer) + dump("XSL processing instruction target");//$NON-NLS-1$ + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 656: break; + case 580: + case 583: + case 584: + case 585: + case 586: + case 587: + case 588: + { + if(Debug.debugTokenizer) + dump("\nCDATA start");//$NON-NLS-1$ + fStateStack.push(yystate()); + yybegin(ST_CDATA_TEXT); + return XML_CDATA_OPEN; + } + case 657: break; + case 572: + { + if(Debug.debugTokenizer) + dump("jsp:root tag name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return JSP_ROOT_TAG_NAME; + } + case 658: break; + case 563: + { + if(Debug.debugTokenizer) + dump("element");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION); + return XML_ELEMENT_DECLARATION; + } + case 659: break; + case 562: + { + if(Debug.debugTokenizer) + dump("attlist");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION); + return XML_ATTLIST_DECLARATION; + } + case 660: break; + case 561: + { + if(Debug.debugTokenizer) + dump("doctype");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_DECLARATION); + return XML_DOCTYPE_DECLARATION; + } + case 661: break; + case 546: + { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + fEmbeddedHint = XML_DOCTYPE_EXTERNAL_ID_PUBREF; + yybegin(ST_XML_DOCTYPE_ID_PUBLIC); + return XML_DOCTYPE_EXTERNAL_ID_PUBLIC; + } + case 662: break; + case 545: + { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + fEmbeddedHint = XML_DOCTYPE_EXTERNAL_ID_SYSREF; + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_SYSTEM; + } + case 663: break; + case 539: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 664: break; + case 514: + case 555: + case 556: + { + return JSP_VBL_QUOTED_CONTENT; + } + case 665: break; + case 504: + case 551: + case 552: + { + return JSP_EL_QUOTED_CONTENT; + } + case 666: break; + case 496: + { + if(Debug.debugTokenizer) + dump("\nJSP comment close");//$NON-NLS-1$ + yybegin(YYINITIAL); + return JSP_COMMENT_CLOSE; + } + case 667: break; + case 483: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + if(yylength() > 2) + yypushback(yylength() -2); + if(Debug.debugTokenizer) + dump("VBL in attr value");//$NON-NLS-1$ + yybegin(ST_JSP_VBL); + fELlevel++; + assembleEmbeddedContainer(JSP_VBL_OPEN, new String[]{JSP_VBL_CLOSE}); + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + return PROXY_CONTEXT; + } + case 668: break; + case 482: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + if(yylength() > 2) + yypushback(yylength() -2); + if(Debug.debugTokenizer) + dump("EL in attr value");//$NON-NLS-1$ + yybegin(ST_JSP_EL); + fELlevel++; + assembleEmbeddedContainer(JSP_EL_OPEN, new String[]{JSP_EL_CLOSE}); + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + return PROXY_CONTEXT; + } + case 669: break; + case 476: + { + if(Debug.debugTokenizer) + dump("\nCharRef");//$NON-NLS-1$ + return XML_CHAR_REFERENCE; + } + case 670: break; + case 473: + { + if(Debug.debugTokenizer) + dump("\ncomment start");//$NON-NLS-1$ + fEmbeddedHint = XML_COMMENT_TEXT; + fEmbeddedPostState = ST_XML_COMMENT; + yybegin(ST_XML_COMMENT); + return XML_COMMENT_OPEN; + } + case 671: break; + case 472: + case 491: + case 494: + case 497: + case 498: + case 500: + case 502: + case 505: + case 507: + case 508: + case 510: + case 512: + case 515: + { + /* JSP comment begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(3); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(4); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(4); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(4); + return scanJSPCommentText(); + } + else if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_JSP_COMMENT); + assembleEmbeddedContainer(JSP_COMMENT_OPEN, JSP_COMMENT_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) + yybegin(ST_BLOCK_TAG_SCAN); + return PROXY_CONTEXT; + } + // finished sanity checks + if(yystate()==YYINITIAL) { + // the simple case, just a regular scriptlet out in content + if(Debug.debugTokenizer) + dump("\nJSP comment start");//$NON-NLS-1$ + yybegin(ST_JSP_COMMENT); + return JSP_COMMENT_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP comment start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_COMMENT); + // the comment container itself will act as comment text + fEmbeddedHint = JSP_COMMENT_TEXT; + assembleEmbeddedContainer(JSP_COMMENT_OPEN, JSP_COMMENT_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + /* + * required help for successive embedded regions; mark this one as a + * comment so it will be otherwise ignored but preserved (which is why + * we can't use white-space) + */ + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } + } + case 672: break; + case 381: + case 382: + { + if(Debug.debugTokenizer) + dump("XML processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 673: break; + case 380: + { + if(Debug.debugTokenizer) + dump("comment end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_COMMENT_CLOSE; + } + case 674: break; + case 379: + { + if(Debug.debugTokenizer) + dump("CDATA end");//$NON-NLS-1$ + yybegin(fStateStack.pop()); + return XML_CDATA_CLOSE; + } + case 675: break; + case 378: + { + yybegin(ST_JSP_VBL); + if(yylength() > 2) + yypushback(yylength() - 2); + fELlevel++; + fEmbeddedHint = XML_CONTENT; + fEmbeddedPostState = YYINITIAL; + assembleEmbeddedContainer(JSP_VBL_OPEN, JSP_VBL_CLOSE); + fEmbeddedHint = XML_CONTENT; + yybegin(YYINITIAL); + return PROXY_CONTEXT; + } + case 676: break; + case 377: + { + if(Debug.debugTokenizer) + dump("\nPEReference");//$NON-NLS-1$ + return XML_PE_REFERENCE; + } + case 677: break; + case 376: + { + yybegin(ST_JSP_EL); + if(yylength() > 2) + yypushback(yylength() - 2); + fELlevel++; + fEmbeddedHint = XML_CONTENT; + fEmbeddedPostState = YYINITIAL; + assembleEmbeddedContainer(JSP_EL_OPEN, JSP_EL_CLOSE); + fEmbeddedHint = XML_CONTENT; + yybegin(YYINITIAL); + return PROXY_CONTEXT; + } + case 678: break; + case 373: + { + if(Debug.debugTokenizer) + dump("\nEntityRef");//$NON-NLS-1$ + return XML_ENTITY_REFERENCE; + } + case 679: break; + case 367: + case 407: + case 415: + case 422: + case 427: + case 432: + case 437: + case 443: + case 449: + case 453: + case 458: + case 463: + case 469: + { + /* JSP expression begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(2); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(3); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(3); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(3); + return scanJSPCommentText(); + } + // end sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just an expression out in content + if(Debug.debugTokenizer) + dump("\nJSP expression start");//$NON-NLS-1$ + yybegin(ST_JSP_CONTENT); + return JSP_EXPRESSION_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP expression start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_CONTENT); + assembleEmbeddedContainer(JSP_EXPRESSION_OPEN, JSP_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } + } + case 680: break; + case 366: + case 406: + case 414: + case 421: + case 426: + case 431: + case 436: + case 442: + case 448: + case 452: + case 457: + case 462: + case 468: + { + /* JSP declaration begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(2); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(3); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(3); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(3); + return scanJSPCommentText(); + } + // end sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just a declaration out in content + if(Debug.debugTokenizer) + dump("\nJSP declaration start");//$NON-NLS-1$ + yybegin(ST_JSP_CONTENT); + return JSP_DECLARATION_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP declaration start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_CONTENT); + assembleEmbeddedContainer(JSP_DECLARATION_OPEN, JSP_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } + } + case 681: break; + case 365: + case 405: + case 413: + case 420: + case 425: + case 430: + case 435: + case 441: + case 447: + case 451: + case 456: + case 461: + case 467: + { + /* JSP directive begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(2); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(3); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(3); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(3); + return scanJSPCommentText(); + } + // end sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just a declaration out in content + if(Debug.debugTokenizer) + dump("\nJSP directive start");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_NAME); + return JSP_DIRECTIVE_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP declaration start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_DIRECTIVE_NAME); + assembleEmbeddedContainer(JSP_DIRECTIVE_OPEN, new String[]{JSP_DIRECTIVE_CLOSE, JSP_CLOSE}); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } + } + case 682: break; + case 355: + { + yybegin(ST_JSP_VBL_DQUOTES_END); + return JSP_VBL_QUOTED_CONTENT; + } + case 683: break; + case 351: + { + yybegin(ST_JSP_VBL_SQUOTES_END); + return JSP_VBL_QUOTED_CONTENT; + } + case 684: break; + case 349: + { + fELlevel++; + if(fELlevel == 1) { + return JSP_VBL_OPEN; + } + } + case 685: break; + case 339: + { + yybegin(ST_JSP_EL_DQUOTES_END); + return JSP_EL_QUOTED_CONTENT; + } + case 686: break; + case 335: + { + yybegin(ST_JSP_EL_SQUOTES_END); + return JSP_EL_QUOTED_CONTENT; + } + case 687: break; + case 333: + { + //System.out.println(JSP_EL_CONTENT+ ":[" + yytext() + "]"); + return JSP_EL_CONTENT; + } + case 688: break; + case 332: + { + fELlevel++; + if(fELlevel == 1) { + return JSP_EL_OPEN; + } + } + case 689: break; + case 329: + { + int enterState = yystate(); + yybegin(ST_JSP_DQUOTED_VBL); + assembleEmbeddedContainer(JSP_VBL_OPEN, new String[]{JSP_VBL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_DQUOTE}); + // abort early when an unescaped double quote is found in the VBL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_DQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; + } + case 690: break; + case 328: + { + int enterState = yystate(); + yybegin(ST_JSP_DQUOTED_EL); + assembleEmbeddedContainer(JSP_EL_OPEN, new String[]{JSP_EL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_DQUOTE}); + // abort early when an unescaped double quote is found in the EL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_DQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; + } + case 691: break; + case 326: + { + int enterState = yystate(); + yybegin(ST_JSP_SQUOTED_VBL); + assembleEmbeddedContainer(JSP_VBL_OPEN, new String[]{JSP_VBL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_SQUOTE}); + // abort early when an unescaped single quote is found in the VBL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_SQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; + } + case 692: break; + case 325: + { + int enterState = yystate(); + yybegin(ST_JSP_SQUOTED_EL); + assembleEmbeddedContainer(JSP_EL_OPEN, new String[]{JSP_EL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_SQUOTE}); + // abort early when an unescaped single quote is found in the EL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_SQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; + } + case 693: break; + case 324: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + int incomingState = yystate(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + if(Debug.debugTokenizer) + dump("JSP attribute value start - end tag");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedContainer(XML_END_TAG_OPEN, new String[]{XML_TAG_CLOSE,XML_EMPTY_TAG_CLOSE}); + if(yystate() != ST_ABORT_EMBEDDED) + yybegin(incomingState); + return PROXY_CONTEXT; + } + case 694: break; + case 283: + case 295: + case 301: + { + return XML_DOCTYPE_INTERNAL_SUBSET; + } + case 695: break; + case 271: + { + String tagName = yytext().substring(1); + // pushback to just after the opening bracket + yypushback(yylength() - 1); + /* + * If this tag can not be nested or we're already searching for an + * attribute name, equals, or value, return immediately. + */ + if (!isNestable(tagName) || (!fStateStack.empty() && (fStateStack.peek() == ST_XML_ATTRIBUTE_NAME || fStateStack.peek() == ST_XML_EQUALS || fStateStack.peek() == ST_XML_ATTRIBUTE_VALUE))) { + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + if(Debug.debugTokenizer) + dump("tag in place of attr value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + // embedded container should be looking for the name (again) next + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedTagSequence(XML_TAG_OPEN, tagName); // ? + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + return PROXY_CONTEXT; + } + case 696: break; + case 269: + { + String tagName = yytext().substring(1); + // pushback to just after the opening bracket + yypushback(yylength() - 1); + /* + * If this tag can not be nested or we're already searching for an + * attribute name, equals, or value, return immediately. + */ + if (!isNestable(tagName) || (!fStateStack.empty() && (fStateStack.peek() == ST_XML_ATTRIBUTE_NAME || fStateStack.peek() == ST_XML_EQUALS || fStateStack.peek() == ST_XML_ATTRIBUTE_VALUE))) { + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + if(Debug.debugTokenizer) + dump("tag in place of attr name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + // embedded container should be looking for the name (again) next + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedTagSequence(XML_TAG_OPEN, tagName); // ? + fStateStack.pop(); + yybegin(ST_XML_EQUALS); + return PROXY_CONTEXT; + } + case 697: break; + case 267: + { + yybegin(YYINITIAL); + fEmbeddedHint = UNDEFINED; + if(Debug.debugTokenizer) + dump("empty tag close");//$NON-NLS-1$ + return XML_EMPTY_TAG_CLOSE; + } + case 698: break; + case 125: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + yybegin(ST_XML_ATTRIBUTE_VALUE_DQUOTED); + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("JSP attribute value start - complex double quoted");//$NON-NLS-1$ + assembleEmbeddedContainer(XML_TAG_ATTRIBUTE_VALUE_DQUOTE, XML_TAG_ATTRIBUTE_VALUE_DQUOTE); + fStateStack.pop(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return PROXY_CONTEXT; + } + case 699: break; + case 123: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("JSP tag embedded name start - start tag");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedContainer(XML_TAG_OPEN, new String[]{XML_TAG_CLOSE,XML_EMPTY_TAG_CLOSE}); + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + return PROXY_CONTEXT; + } + case 700: break; + case 122: + case 127: + case 128: + case 273: + case 277: + case 278: + case 387: + case 390: + case 481: + { + if(Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 701: break; + case 121: + { + if(Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 702: break; + case 120: + { + if(Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 703: break; + case 116: + case 117: + case 118: + case 268: + case 386: + case 480: + case 520: + case 521: + case 540: + case 541: + case 559: + case 560: + case 573: + case 582: + case 590: + case 592: + case 594: + case 596: + case 599: + case 605: + case 606: + case 607: + case 608: + case 609: + case 615: + case 616: + case 617: + case 618: + case 619: + case 625: + case 626: + case 628: + case 629: + case 635: + case 636: + case 637: + case 638: + case 644: + case 645: + case 646: + case 647: + case 650: + case 651: + case 653: + { + if(Debug.debugTokenizer) + dump("tag name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 704: break; + case 114: + { + if(Debug.debugTokenizer) + dump("tag close");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + if(isBlockMarker()) { + fEmbeddedHint = getBlockMarkerContext(); + fEmbeddedPostState = ST_BLOCK_TAG_SCAN; + yybegin(ST_BLOCK_TAG_SCAN); + } + else + yybegin(YYINITIAL); + return XML_TAG_CLOSE; + } + case 705: break; + case 107: + case 111: + case 263: + { + if(Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 706: break; + case 106: + { + if(Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 707: break; + case 105: + { + if(Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 708: break; + case 102: + { + if(Debug.debugTokenizer) + dump("JSP directive name");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_NAME_WHITESPACE); + return JSP_DIRECTIVE_NAME; + } + case 709: break; + case 98: + case 99: + case 100: + { + if(Debug.debugTokenizer) + dump("JSP code content");//$NON-NLS-1$ + return doScan("%>", false, false, false, JSP_CONTENT, ST_JSP_CONTENT, ST_JSP_CONTENT); + } + case 710: break; + case 94: + case 96: + case 97: + case 253: + case 254: + case 257: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 711: break; + case 93: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction '='");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_DHTML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 712: break; + case 92: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_DHTML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 713: break; + case 90: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 714: break; + case 84: + case 86: + case 244: + { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 715: break; + case 83: + { + if(Debug.debugTokenizer) + dump("XML processing instruction '='");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_PI_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 716: break; + case 49: + case 199: + case 200: + case 203: + case 213: + case 214: + case 217: + case 218: + case 360: + case 363: + case 446: + case 460: + case 466: + { + return JSP_VBL_CONTENT; + } + case 717: break; + case 42: + case 178: + case 179: + case 182: + case 192: + case 193: + case 196: + case 197: + case 330: + case 344: + case 347: + case 418: + case 419: + case 434: + case 440: + { + return JSP_EL_CONTENT; + } + case 718: break; + case 35: + case 159: + case 160: + case 321: + case 412: + case 416: + case 495: + case 528: + case 548: + case 565: + case 575: + { + if(Debug.debugTokenizer) + dump("attlist contentspec");//$NON-NLS-1$ + return XML_ATTLIST_DECL_CONTENT; + } + case 719: break; + case 33: + case 152: + case 153: + case 311: + case 404: + case 408: + case 492: + case 527: + case 547: + case 564: + case 574: + { + if(Debug.debugTokenizer) + dump("elementdecl contentspec");//$NON-NLS-1$ + return XML_ELEMENT_DECL_CONTENT; + } + case 720: break; + case 22: + case 112: + { + if(Debug.debugTokenizer) + dump("inappropriate tag name");//$NON-NLS-1$ + if(!fStateStack.empty() && (fStateStack.peek()==ST_XML_ATTRIBUTE_VALUE_SQUOTED||fStateStack.peek()==ST_XML_ATTRIBUTE_VALUE_DQUOTED)) { + yybegin(ST_ABORT_EMBEDDED); + yypushback(yylength()-1); + return XML_TAG_ATTRIBUTE_VALUE; + } + yybegin(YYINITIAL); + return XML_CONTENT; + } + case 721: break; + case 18: + case 104: + { + if(Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_ATTRIBUTE_NAME); + return WHITE_SPACE; + } + case 722: break; + case 5: + case 8: + case 9: + case 10: + case 12: + case 13: + case 14: + case 15: + case 17: + case 19: + case 20: + case 21: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 34: + case 40: + case 41: + case 72: + case 169: + case 174: + { + if(Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + return WHITE_SPACE; + } + case 723: break; + case 0: + case 56: + case 59: + case 61: + case 225: + case 227: + case 228: + case 230: + case 232: + case 370: + case 371: + case 372: + case 475: + { + if(Debug.debugTokenizer) + dump("\nXML content");//$NON-NLS-1$ + return XML_CONTENT; + } + case 724: break; + case 57: + case 101: + case 113: + case 119: + case 129: + { + if(Debug.debugTokenizer) + dump("\nstart tag open");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + case 725: break; + case 58: + case 60: + case 64: + case 65: + case 66: + case 70: + case 71: + case 81: + case 85: + case 87: + case 88: + case 89: + case 91: + case 95: + case 103: + case 108: + case 109: + case 110: + case 115: + case 124: + case 131: + case 132: + case 133: + case 134: + case 136: + case 137: + case 139: + case 140: + case 141: + case 144: + case 145: + case 146: + case 149: + case 150: + case 151: + case 156: + case 157: + case 158: + case 166: + case 171: + case 172: + case 176: + case 177: + case 184: + case 185: + case 187: + case 188: + case 194: + case 198: + case 205: + case 206: + case 208: + case 209: + case 215: + case 219: + { + if (Debug.debugTokenizer) + System.out.println("!!!unexpected!!!: \"" + yytext() + "\":" + //$NON-NLS-2$//$NON-NLS-1$ + yychar + "-" + (yychar + yylength()));//$NON-NLS-1$ + return UNDEFINED; + } + case 726: break; + case 62: + case 63: + { + if(Debug.debugTokenizer) + dump("CDATA text");//$NON-NLS-1$ + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + String returnedContext = doScan("]]>", false, true, true, XML_CDATA_TEXT, ST_CDATA_END, ST_CDATA_END);//$NON-NLS-1$ + if(returnedContext == XML_CDATA_TEXT) + yybegin(ST_CDATA_END); + return returnedContext; + } + case 727: break; + case 67: + case 186: + case 189: + case 207: + case 210: + { + if(Debug.debugTokenizer) + dump("LINE FEED");//$NON-NLS-1$ + return WHITE_SPACE; + } + case 728: break; + case 68: + case 69: + { + if(Debug.debugTokenizer) + dump("comment content");//$NON-NLS-1$ + return scanXMLCommentText(); + } + case 729: break; + case 73: + case 74: + case 75: + case 76: + case 237: + case 238: + case 239: + case 383: + case 478: + case 479: + case 518: + case 519: + case 538: + case 558: + case 571: + case 581: + case 589: + case 591: + case 593: + case 595: + { + if(Debug.debugTokenizer) + dump("processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_CONTENT; + yybegin(ST_PI_WS); + return XML_TAG_NAME; + } + case 730: break; + case 77: + { + yybegin(ST_PI_CONTENT); + return WHITE_SPACE; + } + case 731: break; + case 78: + case 79: + case 80: + { + // block scan until close is found + return doScan("?>", false, false, false, XML_PI_CONTENT, ST_XML_PI_TAG_CLOSE, ST_XML_PI_TAG_CLOSE); + } + case 732: break; + case 82: + { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_XML_PI_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 733: break; + case 126: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + yybegin(ST_XML_ATTRIBUTE_VALUE_SQUOTED); + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("JSP attribute value start - complex single quoted");//$NON-NLS-1$ + assembleEmbeddedContainer(XML_TAG_ATTRIBUTE_VALUE_SQUOTE, XML_TAG_ATTRIBUTE_VALUE_SQUOTE); + fStateStack.pop(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return PROXY_CONTEXT; + } + case 734: break; + case 130: + { + if(Debug.debugTokenizer) + dump("declaration end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 735: break; + case 135: + { + if(Debug.debugTokenizer) + dump("doctype type");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_EXTERNAL_ID); + return XML_DOCTYPE_NAME; + } + case 736: break; + case 138: + case 142: + case 288: + case 292: + case 399: + { + if(Debug.debugTokenizer) + dump("doctype public reference");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_PUBREF; + } + case 737: break; + case 143: + case 147: + case 298: + { + if(Debug.debugTokenizer) + dump("doctype system reference");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_DECLARATION_CLOSE); + return XML_DOCTYPE_EXTERNAL_ID_SYSREF; + } + case 738: break; + case 148: + case 304: + case 308: + case 402: + { + if(Debug.debugTokenizer) + dump("elementdecl name");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_ELEMENT_DECLARATION_CONTENT); + return XML_ELEMENT_DECL_NAME; + } + case 739: break; + case 154: + { + if(Debug.debugTokenizer) + dump("elementdecl close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 740: break; + case 155: + case 314: + case 318: + case 410: + { + if(Debug.debugTokenizer) + dump("attlist name");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_ATTLIST_DECLARATION_CONTENT); + return XML_ATTLIST_DECL_NAME; + } + case 741: break; + case 161: + { + if(Debug.debugTokenizer) + dump("attlist close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 742: break; + case 164: + case 165: + { + if(Debug.debugTokenizer) + dump("\nJSP comment text");//$NON-NLS-1$ + return scanJSPCommentText(); + } + case 743: break; + case 167: + case 173: + { + return XML_TAG_ATTRIBUTE_VALUE; + } + case 744: break; + case 168: + { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + int incomingState = yystate(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + if(Debug.debugTokenizer) + dump("tag inside of JSP attribute value start");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedContainer(XML_TAG_OPEN, new String[]{XML_TAG_CLOSE,XML_EMPTY_TAG_CLOSE}); + if(yystate() != ST_ABORT_EMBEDDED) + yybegin(incomingState); + return PROXY_CONTEXT; + } + case 745: break; + case 170: + { + return XML_TAG_ATTRIBUTE_VALUE_SQUOTE; + } + case 746: break; + case 175: + { + return XML_TAG_ATTRIBUTE_VALUE_DQUOTE; + } + case 747: break; + case 180: + { + yybegin(ST_JSP_EL_DQUOTES); + return JSP_EL_DQUOTE; + } + case 748: break; + case 181: + { + yybegin(ST_JSP_EL_SQUOTES); + return JSP_EL_SQUOTE; + } + case 749: break; + case 183: + { + fELlevel--; + if(fELlevel == 0) { + yybegin(YYINITIAL); + return JSP_EL_CLOSE; + } + return JSP_EL_CONTENT; + } + case 750: break; + case 190: + { + yybegin(ST_JSP_EL); + return JSP_EL_SQUOTE; + } + case 751: break; + case 191: + { + yybegin(ST_JSP_EL); + return JSP_EL_DQUOTE; + } + case 752: break; + case 195: + { + return JSP_EL_CLOSE; + } + case 753: break; + case 201: + { + yybegin(ST_JSP_VBL_DQUOTES); + return JSP_VBL_DQUOTE; + } + case 754: break; + case 202: + { + yybegin(ST_JSP_VBL_SQUOTES); + return JSP_VBL_SQUOTE; + } + case 755: break; + case 204: + { + fELlevel--; + if(fELlevel == 0) { + yybegin(YYINITIAL); + return JSP_VBL_CLOSE; + } + return JSP_VBL_CONTENT; + } + case 756: break; + case 211: + { + yybegin(ST_JSP_VBL); + return JSP_VBL_SQUOTE; + } + case 757: break; + case 212: + { + yybegin(ST_JSP_VBL); + return JSP_VBL_DQUOTE; + } + case 758: break; + case 216: + { + return JSP_VBL_CLOSE; + } + case 759: break; + case 220: + { + if(Debug.debugTokenizer) + dump("\nend tag open");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_TAG_NAME); + return XML_END_TAG_OPEN; + } + case 760: break; + case 221: + { + if(Debug.debugTokenizer) + dump("\nprocessing instruction start");//$NON-NLS-1$ + yybegin(ST_PI); + return XML_PI_OPEN; + } + case 761: break; + case 222: + case 310: + case 320: + case 331: + case 336: + case 340: + case 343: + case 346: + case 348: + case 352: + case 356: + case 359: + case 362: + { + /* JSP scriptlet begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(1); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(2); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(2); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(2); + return scanJSPCommentText(); + } + // finished sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just a regular scriptlet out in content + if(Debug.debugTokenizer) + dump("\nJSP scriptlet start");//$NON-NLS-1$ + yybegin(ST_JSP_CONTENT); + return JSP_SCRIPTLET_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint);//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP scriptlet start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_CONTENT); + assembleEmbeddedContainer(JSP_SCRIPTLET_OPEN, JSP_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } + } + case 762: break; + case 223: + { + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("\ndeclaration start");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION); + return XML_DECLARATION_OPEN; + } + case 763: break; + case 236: + { + if(Debug.debugTokenizer) + dump("processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 764: break; + case 240: + { + // ended with nothing inside + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 765: break; + case 241: + { + if(Debug.debugTokenizer) + dump("XML processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 766: break; + case 258: + { + if(Debug.debugTokenizer) + dump("JSP end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return JSP_CLOSE; + } + case 767: break; + case 260: + { + if(Debug.debugTokenizer) + dump("JSP end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return JSP_DIRECTIVE_CLOSE; + } + case 768: break; + case 162: + case 163: + { + return doBlockTagScan(); + } + case 769: break; + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + return null; + } + else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.jflex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.jflex new file mode 100644 index 0000000..0c21b1c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/JSPTokenizer.jflex
@@ -0,0 +1,2859 @@ +/******************************************************************************* + * Copyright (c) 2004, 2011 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 + * Frits Jalvingh - contributions for bug 150794 + *******************************************************************************/ + +package org.eclipse.jst.jsp.core.internal.parser.internal; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jst.jsp.core.internal.Logger; +import org.eclipse.jst.jsp.core.internal.contenttype.IntStack; +import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTokenizer; +import org.eclipse.wst.sse.core.internal.ltk.parser.TagMarker; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.eclipse.wst.xml.core.internal.parser.ContextRegionContainer; + +%% + +%{ + private int fTokenCount = 0; + + // required holders for white-space compacting + private boolean fShouldLoadBuffered = false; + private String fBufferedContext = null; + private int fBufferedStart = 1; + private int fBufferedLength = 0; + private ITextRegion fBufferedEmbeddedContainer = null; + private ITextRegion fProxyUnknownRegion = null; + private String f_context = null; + + // state stack for handling embedded regions + private IntStack fStateStack = new IntStack(); + // a "hint" as to what an embedded region should be evaluated + private String fEmbeddedHint = UNDEFINED; + // a "hint" as to what state to enter once an embedded region has + // been completed + private int fEmbeddedPostState = YYINITIAL; + + // the container used to create embedded regions + private ContextRegionContainer fEmbeddedContainer = null; + private static final String PROXY_CONTEXT = "PROXY_CONTEXT"; + private static final String PROXY_UNKNOWN_CONTEXT = "PROXY_UNKNOWN_CONTEXT"; + + private String context = null; + private int start = 0; + private int textLength = 0; + private int length = 0; + + // offset for tracking position specific block tags + private int fOffset = 0; + + // the name of the current tag being opened + private String fCurrentTagName = null; + + // the name of the current tag inside of an embedded region + private String internalTagName = null; + private String internalContext = null; + + // the list of tag name BlockMarkers + private List fBlockMarkers = new ArrayList(0); + private List fNestablePrefixes = new ArrayList(1); + + // where the last internal container block was found + private int fLastInternalBlockStart = -1; + + // required to not seek text blocks on an end tag + private boolean fIsBlockingEnabled = false; + private boolean fIsCaseSensitiveBlocking = true; + + private static final boolean fForbidJSP = false; + + private int fELlevel = 0; + + private JSPParserRegionFactory fRegionFactory = new JSPParserRegionFactory(); + + // Is the embedded tag a JSP tag + private boolean fEmbeddedTag = false; + // Is the non-embedded tag a JSP tag + private boolean fContainerTag = false; + // Is the tokenizer in a non-embedded tag (between < and >) + private boolean fInTagContainer = false; + // Is the tokenizer in an embedded tag (between < and >) + private boolean fInTagEmbedded = false; + + /** + * user method + */ + public final void addBlockMarker(BlockMarker marker) { + if(containsTagName(marker.getTagName())) + return; + fBlockMarkers.add(marker); + } + /** + * user method + */ + public final void addNestablePrefix(TagMarker marker) { + fNestablePrefixes.add(marker); + } + /* user method */ + public List getNestablePrefixes() { + return fNestablePrefixes; + } + /** + * user method + */ + private boolean isNestable(String tagName) { + //Iterator blocks = fNestablePrefixes.iterator(); + //while(blocks.hasNext()) { + // TagMarker marker = (TagMarker)blocks.next(); + // String markerName = marker.getTagName(); + // if(tagName.length() > markerName.length() + 1 && tagName.startsWith(markerName) && tagName.charAt(markerName.length()) == ':') { + // return marker.isGlobal() || getOffset() >= marker.getMarker().getStart(); + // } + //} + //return false; + return true; + } + /** + * user method + */ + public final void removeNestablePrefix(String name) { + if (fNestablePrefixes != null) { + Iterator nestables = fNestablePrefixes.iterator(); + while (nestables.hasNext()) { + if (((TagMarker) nestables.next()).getTagName().equalsIgnoreCase(name)) + nestables.remove(); + } + } + } + /** + * user method + */ + public final void removeBlockMarker(BlockMarker marker) { + fBlockMarkers.remove(marker); + } + /** + * user method + */ + public final void removeBlockMarker(String tagname) { + if (fBlockMarkers != null) { + Iterator blocks = fBlockMarkers.iterator(); + while (blocks.hasNext()) { + if (((BlockMarker) blocks.next()).getTagName().equals(tagname)) + blocks.remove(); + } + } + } + /* user method */ + private final void assembleEmbeddedTagSequence(String startType, String endTagName) { + assembleEmbeddedContainer(startType, null, endTagName); + } + /* user method */ + private final void assembleEmbeddedContainer(String startType, String[] endTypes) { + assembleEmbeddedContainer(startType, endTypes, null); + } + /* user method */ + private final void assembleEmbeddedContainer(String startType, String endType) { + assembleEmbeddedContainer(startType, new String[]{endType}, null); + } + /** + * user method + * + * Assembles an embedded container beginning with the given startType as + * the first ContextRegion within it and of the type fEmbeddedHint. The + * endTypes[] array contains the context types that will cause a successful + * exit. Use of the endTagName parameter alters this behavior to force an + * exit on an XML_TAG_CLOSE after seeing an XML_TAG_NAME whose significant + * text matches the endTagName String. All contents in between are + * insignificant, and yes, this means comments are allowed inside. + **/ + private final void assembleEmbeddedContainer(String startType, String[] endTypes, String endTagName) { + // the context of the region being added to the embedded container + internalContext = startType; + // keep track of where this container began; to provide relative indeces for the regions + int containerStart = yychar; + boolean notFinished = true; + // keep track of where we seem to be so that the endTagName can be checked + boolean isInEndTag = false; + boolean isInFirstTag = true; + // create the embedded container and setup its "type" + if (fEmbeddedContainer == null) { + fEmbeddedContainer = new ContextRegionContainer(); + fEmbeddedContainer.setType(fEmbeddedHint); + fEmbeddedContainer.setStart(containerStart); + // TODO: parent region needs to be set .... but not sure where to get it from + // fEmbeddedContainer.setParent(parentRegion); + } + int initialLength = fEmbeddedContainer.getRegions().size(); + containerStart = fEmbeddedContainer.getStart(); + while (notFinished) { + // add the region to the container + if (internalContext != null && internalContext != PROXY_CONTEXT) { + ITextRegion newToken = fRegionFactory.createToken(internalContext, yychar - containerStart, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + fEmbeddedContainer.setLength(fEmbeddedContainer.getLength() + yylength()); + fEmbeddedContainer.setTextLength(fEmbeddedContainer.getTextLength() + yylength()); + // DW, 4/16/2003 token regions no longer have parents + //newToken.setParent(fEmbeddedContainer); + } + try { + // longscan determines whether to attempt a blockTagScan within the embedded container + boolean longscan = false; + // save the tokenizer state in case of a block tag scan + int previousState = yystate(); + String previousCurrentTagName = fCurrentTagName; + int previousPostState = fEmbeddedPostState; + String previousEmbeddedHint = fEmbeddedHint; + // determine if a block tag scan is necessary + if (internalContext == XML_TAG_NAME) { + internalTagName = yytext(); + if (endTagName != null && endTagName.length() == 0){ + endTagName = internalTagName; + } + if(!isNestable(internalTagName)) { + internalTagName = null; + // snagged a tag name we shouldn't have + fEmbeddedPostState = ST_ABORT_EMBEDDED; + notFinished = false; + } + } + else if (internalContext == XML_TAG_OPEN || internalContext == XML_END_TAG_OPEN) { + internalTagName = null; + } + // do upkeep for endTagName usage; must be here since the next token could be the close + if (internalContext == XML_END_TAG_OPEN) { + isInEndTag = true; + } else if (internalContext == XML_TAG_CLOSE) { + isInFirstTag = isInEndTag = false; + } else { + ITextRegionList embeddedRegions = fEmbeddedContainer.getRegions(); + if (embeddedRegions.size() > 2 && (embeddedRegions.get(embeddedRegions.size()-1)).getType() == XML_TAG_CLOSE && (embeddedRegions.get(embeddedRegions.size() - 3)).getType() == XML_TAG_OPEN && internalTagName != null) { + if (containsTagName(internalTagName)) { + longscan = true; + yybegin(ST_BLOCK_TAG_SCAN); + } + } + } + if (longscan) + fCurrentTagName = internalTagName; + // read the next region and context + internalContext = primGetNextToken(); + if (longscan) { + // Returning from a block tag scan requires restoring some state variables + // as well as handling the block region and setting up for normal scanning + // inside the embedded container + ITextRegion newToken = fRegionFactory.createToken(internalContext, yychar - containerStart, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + fEmbeddedContainer.setLength(fEmbeddedContainer.getLength() + yylength()); + fEmbeddedContainer.setTextLength(fEmbeddedContainer.getTextLength() + yylength()); + // DW, 4/16/2003 token regions no longer have parents + // newToken.setParent(fEmbeddedContainer); + longscan = false; + fEmbeddedPostState = previousPostState; + fEmbeddedHint = previousEmbeddedHint; + fCurrentTagName = previousCurrentTagName; + yybegin(previousState); + internalContext = primGetNextToken(); + } + } catch (IOException e) { + // primGetNextToken() calls may throw an IOException + // catch and do nothing since the isEOF check below + // will properly exit if the input was too short + } catch (Exception f) { + // some other exception happened; never should + Logger.logException(f); + } + boolean isEndingType = yystate() == ST_ABORT_EMBEDDED; + ITextRegionList embeddedList = fEmbeddedContainer.getRegions(); + if(!isEndingType) { + // check for ending context + if (endTagName == null) { + for (int i = 0; i < endTypes.length; i++) { + isEndingType = isEndingType || (internalContext == endTypes[i]) || (embeddedList.size() - initialLength) >= 2 && (embeddedList.get(embeddedList.size()-1)).getType() == endTypes[i]; + } + } + else { + isEndingType = ((isInEndTag && internalContext == XML_TAG_CLOSE) || (isInFirstTag && internalContext == XML_EMPTY_TAG_CLOSE)) && internalTagName != null && internalTagName.equals(endTagName); + } + } + notFinished = notFinished && ((!isEndingType) && !isEOF() && (endTagName != null || internalContext != UNDEFINED) && !(internalContext == PROXY_CONTEXT && (embeddedList.get(embeddedList.size()-1)).getType() == UNDEFINED)); + } + // finish adding the last context + if (internalContext != null && internalContext != PROXY_CONTEXT) { + ITextRegion newToken = fRegionFactory.createToken(internalContext, yychar - containerStart, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + // DW, 4/16/2003 token regions no longer have parents + //newToken.setParent(fEmbeddedContainer); + fEmbeddedContainer.setLength(yychar - containerStart + yylength()); + fEmbeddedContainer.setTextLength(yychar - containerStart + yylength()); + } + yybegin(fEmbeddedPostState); + } + /* user method */ + public final boolean isCaseSensitiveBlocking() { + return fIsCaseSensitiveBlocking; + } + /* user method */ + public final void setCaseSensitiveBlocking(boolean newValue) { + fIsCaseSensitiveBlocking = newValue; + } + /* user method */ + public boolean getBlockMarkerAllowsJSP() { + return getBlockMarkerAllowsJSP(fCurrentTagName); + } + /* user method */ + public boolean getBlockMarkerAllowsJSP(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if(casesensitive && marker.getTagName().equals(name)) + return marker.allowsJSP(); + else if(!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return marker.allowsJSP(); + } + return true; + } + /* user method */ + public boolean getBlockMarkerCaseSensitivity() { + return getBlockMarkerCaseSensitivity(fCurrentTagName); + } + public boolean getBlockMarkerCaseSensitivity(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if(casesensitive && marker.getTagName().equals(name)) + return casesensitive; + else if(!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return casesensitive; + } + return true; + } + /* user method */ + public String getBlockMarkerContext() { + return getBlockMarkerContext(fCurrentTagName); + } + /* user method */ + public String getBlockMarkerContext(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + if(marker.getTagName().equals(name)) + return marker.getContext(); + } + return BLOCK_TEXT; + } + /* user method */ + public List getBlockMarkers() { + return fBlockMarkers; + } + /* user method */ + public final int getOffset() { + return fOffset + yychar; + } + private final boolean isBlockMarker() { + return isBlockMarker(fCurrentTagName); + } + private final boolean isBlockMarker(String tagName) { + if (!fIsBlockingEnabled) + return false; + return containsTagName(tagName); + } + /** + * user method + */ + public final void beginBlockTagScan(String newTagName) { + beginBlockMarkerScan(newTagName, BLOCK_TEXT); + } + /** + * user method + * + * Special tokenizer setup. Allows tokenization to be initiated at the + * start of a text block within a "newTagName" tag. + * + * Example: + * Tokenizer toker = new Tokenizer(); + * toker.setCaseSensitiveBlocking(false); + * toker.reset(new java.io.StringReader("afiuhqwkejhtasihgalkwhtq</scripter></scr></script>asgdasga")); + * toker.beginBlockMarkerScan("script", BLOCK_TEXT); + * toker.getRegions(); + * + * Returns: + * BLOCK_TEXT: 0-40 + * XML_END_TAG_OPEN: 41-42 + * XML_TAG_NAME: 43-48 + * XML_TAG_CLOSE: 49-49 + * XML_CONTENT: 50-57 + * + */ + public final void beginBlockMarkerScan(String newTagName, String blockcontext) { + yybegin(ST_BLOCK_TAG_SCAN); + fCurrentTagName = newTagName; + } + +/** + * Method doScan. + * + * Returns a context region for all of the text from the current position upto the end of input or + * to right *before* the first occurence of searchString + * + * @param searchString - target string to search for ex.: "-->", "</tagname" + * @param requireTailSeparator - whether the target must be immediately followed by whitespace or '>' + * @param allowJSP - check for and allow for JSP markup <%%> + * @param context - the context of the scanned region if non-zero length + * @param exitState - the state to go to if the region was of non-zero length + * @param abortState - the state to go to if the searchString was found immediately + * @return String - the context found: the desired context on a non-zero length match, the abortContext on immediate success + * @throws IOException + */ +private final String doScan(String searchString, boolean requireTailSeparator, boolean allowJSP, boolean allowCDATA, String searchContext, int exitState, int immediateFallbackState) throws IOException { + boolean stillSearching = true; + boolean wasBlockingEnabled = fIsBlockingEnabled; + try { + // Disable further block (probably) + fIsBlockingEnabled = false; + int searchStringLength = searchString.length(); + int n = 0; + char lastCheckChar; + int i; + boolean same = false; + // Check for JSP starts ("<%") if the tag is global like SCRIPT or STYLE + boolean checkJSPs = allowJSP && !fForbidJSP; + boolean checkedForJSPsOnce = !checkJSPs; + boolean checkedJSPsAtStartOnce = false; + + while (stillSearching) { + n = 0; + // Ensure that enough data from the input exists to compare against the search String. + n = yy_advance(); + while(n != YYEOF && yy_currentPos < searchStringLength) + n = yy_advance(); + // c = (char) n; + // If the input was too short or we've exhausted the input, stop immediately. + if (n == YYEOF && checkedForJSPsOnce) { + stillSearching = false; + } + else { + /** + * Look for starting JSPs "<%" + */ + checkedForJSPsOnce = true; + // 1) yy_currentPos - searchStringLength : There's at least searchStringLength of input available; once that's read, check for JSPs + // --- + // Look for a JSP beginning at current-searchStringLength; if so, backup and switch scanner states to handle it. + // Ensure that we've not encountered a complete block (<%%>) that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first scan would have come out this far anyway]. + if(checkJSPs && yy_currentPos > searchStringLength && yy_currentPos - searchStringLength != fLastInternalBlockStart && + yy_buffer[yy_currentPos - searchStringLength] == '<' && yy_buffer[yy_currentPos - searchStringLength + 1] == '%') { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + // 2) yy_currentPos - jspstarter.length : There's not searchStringLength of input available; check for a JSP 2 spots back in what we could read + // --- + // Look for a JSP beginning at the current position; this case wouldn't be handled by the preceding section + // since it relies upon *having* closeTagStringLength amount of input to work as designed. Must be sure we don't + // spill over the end of the buffer while checking. + else if(checkJSPs && yy_startRead != fLastInternalBlockStart && yy_currentPos > 0 && yy_currentPos < yy_buffer.length - 1 && + yy_buffer[yy_currentPos - 1] == '<' && yy_buffer[yy_currentPos] == '%') { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - 1; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + // 3) yy_currentPos..(yy_currentPos+jspStartlength-1) : Check at the start of the block one time + // --- + // Look for a JSP beginning immediately in the block area; this case wouldn't be handled by the preceding section + // since it relies upon yy_currentPos equaling exactly the previous end +1 to work as designed. + else if(checkJSPs && !checkedJSPsAtStartOnce && yy_startRead != fLastInternalBlockStart && yy_startRead > 0 && + yy_startRead < yy_buffer.length - 1 && yy_buffer[yy_startRead] == '<' && yy_buffer[yy_startRead + 1] == '%') { + checkedJSPsAtStartOnce = true; + fLastInternalBlockStart = yy_markedPos = yy_startRead; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + + + /** + * Look for starting CDATA "<![CDATA[" + */ + // 1) yy_currentPos - searchStringLength: There's at least searchStringLength of input available; once that's read, check for CDATA + // --- + // Look for a CDATA beginning at current-searchStringLength; if so, backup and switch scanner states to handle it. + // Ensure that we've not encountered a complete block (<[!CDATA[]]>) that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first scan would have come out this far anyway]. + /* if(checkCDATA && yy_currentPos > searchStringLength && yy_currentPos + searchStringLength < yy_buffer.length && yy_currentPos - searchStringLength != fLastInternalBlockStart && + charsMatch(cdataStarter, yy_buffer, 0, yy_currentPos - searchStringLength)) { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + // go to a state where CDATA can be found + if (fEmbeddedContainer == null) { + fEmbeddedContainer = new ContextRegionContainer(); + fEmbeddedContainer.setType(searchContext); + fEmbeddedContainer.setStart(yychar); + } + ITextRegion newToken = fRegionFactory.createToken(searchContext, yychar, yylength(), yylength()); + fEmbeddedContainer.getRegions().add(newToken); + fEmbeddedContainer.setLength(fEmbeddedContainer.getLength() + yylength()); + fEmbeddedContainer.setTextLength(fEmbeddedContainer.getTextLength() + yylength()); + yybegin(YYINITIAL); + String context = primGetNextToken(); + if(context.equals(XMLRegionContexts.XML_CDATA_OPEN)) { + assembleEmbeddedContainer(XMLRegionContexts.XML_CDATA_OPEN, XMLRegionContexts.XML_CDATA_CLOSE); + } + yybegin(resumeState); + return searchContext; + } + *//* + // 2) yy_currentPos - cdataStarter.length: There's not searchStringLength of input available; check for a CDATA right here spots back in what we could read + // --- + // Look for a JSP beginning at the current position; this case wouldn't be handled by the preceding section + // since it relies upon *having* closeTagStringLength amount of input to work as designed. Must be sure we don't + // spill over the end of the buffer while checking. + else if(checkCDATA && yy_startRead != fLastInternalBlockStart && yy_currentPos > 0 && yy_currentPos < yy_buffer.length - 1 && + yy_buffer[yy_currentPos - 1] == '<' && yy_buffer[yy_currentPos] == '%') { + fLastInternalBlockStart = yy_markedPos = yy_currentPos - 1; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + // 3) yy_currentPos : Check at the start of the block one time + // --- + // Look for a JSP beginning immediately in the block area; this case wouldn't be handled by the preceding section + // since it relies upon yy_currentPos equaling exactly the previous end +1 to work as designed. + else if(checkCDATA && !checkedForCDATAOnce && yy_startRead != fLastInternalBlockStart && yy_startRead > 0 && + yy_startRead < yy_buffer.length - 1 && yy_buffer[yy_startRead] == '<' && yy_buffer[yy_startRead + 1] == '%') { + checkedForCDATAOnce = true; + fLastInternalBlockStart = yy_markedPos = yy_startRead; + yy_currentPos = yy_markedPos + 1; + int resumeState = yystate(); + yybegin(ST_BLOCK_TAG_INTERNAL_SCAN); + if(yy_markedPos == yy_startRead) { + String jspContext = primGetNextToken(); + yybegin(resumeState); + return jspContext; + } + return searchContext; + } + */ + // Check the characters in the target versus the last targetLength characters read from the buffer + // and see if it matches + if (n == YYEOF) { + stillSearching = false; + } + else { + same = true; + // safety check for array accesses + if(yy_currentPos >= searchStringLength && yy_currentPos <= yy_buffer.length) { + for(i = 0; i < searchStringLength && same; i++) { + if(fIsCaseSensitiveBlocking) + same = yy_buffer[i + yy_currentPos - searchStringLength] == searchString.charAt(i); + else + same = Character.toLowerCase(yy_buffer[i + yy_currentPos - searchStringLength]) == Character.toLowerCase(searchString.charAt(i)); + } + } + // safety check failed; no match is possible right now + else { + same = false; + } + } + if (same && requireTailSeparator && yy_currentPos < yy_buffer.length) { + // Additional check for close tags to ensure that targetString="</script" doesn't match + // "</scriptS" + lastCheckChar = yy_buffer[yy_currentPos]; + // Succeed on "</script>" and "</script " + if(lastCheckChar == '>' || Character.isWhitespace(lastCheckChar)) + stillSearching = false; + } + else { + stillSearching = !same || (yy_currentPos < yy_startRead + searchStringLength); + } + } + } + if (n != YYEOF || same) { + // We've stopped short of the end or definitely found a match + yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + // If the searchString occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if (yy_markedPos == yy_startRead) { + yybegin(immediateFallbackState); + return primGetNextToken(); + } + } + else { + // We ran through the rest of the input + yy_markedPos = yy_currentPos; + yy_currentPos++; + } + yybegin(exitState); + // If the ending occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if(yy_markedPos == yy_startRead) + return primGetNextToken(); + return searchContext; + } + finally { + fIsBlockingEnabled = wasBlockingEnabled; + } +} +/** + * user method + * does a lookahead for the current tag name + */ +private final String doBlockTagScan() throws IOException { + fIsCaseSensitiveBlocking = getBlockMarkerCaseSensitivity(); + return doScan("</" + fCurrentTagName, true, getBlockMarkerAllowsJSP(), true, getBlockMarkerContext(fCurrentTagName), YYINITIAL, YYINITIAL); +} + /** + * user method + * + * Converts the raw context String returned by the primGetNextToken() + * method into a full ITextRegion by pulling in values for the + * current offset within the scanning text. + * + * Returns null when EOF is encountered and attaches intermittently + * discovered whitespace onto the end of useful regions. + * + * Note that this algorithm caches the token following the one being returned + * so that whitespace can be collapsed. + */ + public final ITextRegion getNextToken() throws IOException { + fEmbeddedContainer = null; + // load the starting non-whitespace token (assume that it is so) + if (fShouldLoadBuffered) { + if (fBufferedEmbeddedContainer != null) { + ITextRegion container = fBufferedEmbeddedContainer; + fBufferedEmbeddedContainer = null; + fShouldLoadBuffered = false; + return container; + } + context = fBufferedContext; + start = fBufferedStart; + textLength = length = fBufferedLength; + fShouldLoadBuffered = false; + } else { + context = primGetNextToken(); + if (context == PROXY_CONTEXT) { + return fEmbeddedContainer; + } else if (context == XML_TAG_NAME || f_context == JSP_ROOT_TAG_NAME || f_context == JSP_DIRECTIVE_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } else if (context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } else if (context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + start = yychar; + textLength = length = yylength(); + if (yy_atEOF) { + fTokenCount++; + return null; + } + } + // store the next token + f_context = primGetNextToken(); + if (f_context == PROXY_CONTEXT) { + fBufferedEmbeddedContainer = fEmbeddedContainer; + fShouldLoadBuffered = true; + } else if (f_context == XML_TAG_NAME || f_context == JSP_ROOT_TAG_NAME || f_context == JSP_DIRECTIVE_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } else if (f_context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } else if (f_context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } else if (f_context == PROXY_UNKNOWN_CONTEXT) { + fBufferedEmbeddedContainer = fProxyUnknownRegion; + } + fBufferedContext = f_context; + fBufferedStart = yychar; + fBufferedLength = yylength(); + fShouldLoadBuffered = true; + if (fBufferedContext == WHITE_SPACE) { + fShouldLoadBuffered = false; + length += fBufferedLength; + } + if (context == null) { + // EOF + if (Debug.debugTokenizer) { + System.out.println(getClass().getName() + " discovered " + fTokenCount + " tokens."); //$NON-NLS-2$//$NON-NLS-1$ + } + return null; + } + fTokenCount++; + return fRegionFactory.createToken(context, start, textLength, length, null, fCurrentTagName); + } + /* user method */ + public JSPTokenizer(){ + super(); + } + /* user method */ + public JSPTokenizer(char[] charArray){ + this(new CharArrayReader(charArray)); + } + /* user method */ + public void reset(char[] charArray) { + reset(new CharArrayReader(charArray), 0); + } + /* user method */ + public void reset(char[] charArray, int newOffset) { + reset(new CharArrayReader(charArray), newOffset); + } + /* user method */ + public void reset(java.io.InputStream in) { + reset(new java.io.InputStreamReader(in), 0); + } + /* user method */ + public void reset(java.io.InputStream in, int newOffset) { + reset(new java.io.InputStreamReader(in), newOffset); + } + /* user method */ + public void reset(java.io.Reader in) { + reset(in, 0); + } + /** + * user method * + * + * Reset internal counters and vars to "newly created" values, in the hopes + * that resetting a pre-existing tokenizer is faster than creating a new one. + * + * This method contains code blocks that were essentially duplicated from the + * <em>generated</em> output of this specification before this method was + * added. Those code blocks were under the above copyright. + */ + public void reset(java.io.Reader in, int newOffset) { + if (Debug.debugTokenizer) { + System.out.println("resetting tokenizer");//$NON-NLS-1$ + } + fOffset = newOffset; + + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + //yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + /* user vars: */ + fTokenCount = 0; + + fShouldLoadBuffered = false; + fBufferedContext = null; + fBufferedStart = 1; + fBufferedLength = 0; + fStateStack = new IntStack(); + + fLastInternalBlockStart = -1; + + context = null; + start = 0; + textLength = 0; + length = 0; + + fEmbeddedContainer = null; + + fELlevel = 0; + } + /** + * user method + * + */ + public BlockTokenizer newInstance() { + JSPTokenizer newInstance = new JSPTokenizer(); + // global tagmarkers can be shared; they have no state and + // are never destroyed (e.g. 'release') + for(int i = 0; i < fBlockMarkers.size(); i++) { + BlockMarker blockMarker = (BlockMarker) fBlockMarkers.get(i); + if(blockMarker.isGlobal()) + newInstance.addBlockMarker(blockMarker); + } + for(int i = 0; i < fNestablePrefixes.size(); i++) { + TagMarker marker = (TagMarker) fNestablePrefixes.get(i); + if(marker.isGlobal()) + newInstance.addNestablePrefix(marker); + } + return newInstance; + } + /* user method */ + private final String scanXMLCommentText() throws IOException { + // Scan for '-->' and return the text up to that point as + // XML_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_XML_COMMENT_END state and return the next + // context as usual. + return doScan("-->", false, true, true, XML_COMMENT_TEXT, ST_XML_COMMENT_END, ST_XML_COMMENT_END); + } + /* user method */ + private final String scanJSPCommentText() throws IOException { + // Scan for '--%>' and return the text up to that point as + // JSP_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_JSP_COMMENT_END state and return the next + // context as usual. + return doScan("--%>", false, false, true, JSP_COMMENT_TEXT, ST_JSP_COMMENT_END, ST_JSP_COMMENT_END); + } + + /* user method */ + private boolean isJspTag() { + return (fContainerTag && fEmbeddedContainer != null) || (fContainerTag && fInTagContainer) || (fEmbeddedTag && fInTagEmbedded); + } +%} + +%eof{ +// do nothing, this is the downstream parser's job +%eof} + +%public +%class JSPTokenizer +%implements BlockTokenizer, DOMJSPRegionContexts +%function primGetNextToken +%type String +%char +%unicode +%pack + +%state ST_CDATA_TEXT +%state ST_CDATA_END +%state ST_XML_COMMENT +%state ST_XML_COMMENT_END +%state ST_PI +%state ST_PI_WS +%state ST_PI_CONTENT +%state ST_XML_PI_ATTRIBUTE_NAME +%state ST_XML_PI_EQUALS +%state ST_XML_PI_ATTRIBUTE_VALUE +%state ST_XML_PI_TAG_CLOSE +%state ST_DHTML_ATTRIBUTE_NAME +%state ST_DHTML_EQUALS +%state ST_DHTML_ATTRIBUTE_VALUE +%state ST_DHTML_TAG_CLOSE + +// scriptlet state(s) +%state ST_JSP_CONTENT +%state ST_JSP_DIRECTIVE_NAME +%state ST_JSP_DIRECTIVE_NAME_WHITESPACE +%state ST_JSP_DIRECTIVE_ATTRIBUTE_NAME +%state ST_JSP_DIRECTIVE_EQUALS +%state ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE + +// normal tag states +%state ST_XML_TAG_NAME +%state ST_XML_ATTRIBUTE_NAME +%state ST_XML_EQUALS +%state ST_XML_ATTRIBUTE_VALUE + +// declaration (DTD) states +%state ST_XML_DECLARATION +%state ST_XML_DECLARATION_CLOSE + +%state ST_XML_DOCTYPE_DECLARATION +%state ST_XML_DOCTYPE_EXTERNAL_ID +%state ST_XML_DOCTYPE_ID_PUBLIC +%state ST_XML_DOCTYPE_ID_SYSTEM + +%state ST_XML_ELEMENT_DECLARATION +%state ST_XML_ELEMENT_DECLARATION_CONTENT + +%state ST_XML_ATTLIST_DECLARATION +%state ST_XML_ATTLIST_DECLARATION_CONTENT + + +%state ST_BLOCK_TAG_SCAN +%state ST_BLOCK_TAG_INTERNAL_SCAN + +%state ST_JSP_COMMENT +%state ST_JSP_COMMENT_END + +%state ST_JSP_ATTRIBUTE_VALUE +%state ST_XML_ATTRIBUTE_VALUE_SQUOTED +%state ST_XML_ATTRIBUTE_VALUE_DQUOTED + +%state ST_ABORT_EMBEDDED + +%state ST_JSP_EL +%state ST_JSP_EL_SQUOTES +%state ST_JSP_EL_DQUOTES +%state ST_JSP_EL_SQUOTES_END +%state ST_JSP_EL_DQUOTES_END + +%state ST_JSP_DQUOTED_EL +%state ST_JSP_SQUOTED_EL + +%state ST_JSP_VBL +%state ST_JSP_VBL_SQUOTES +%state ST_JSP_VBL_DQUOTES +%state ST_JSP_VBL_SQUOTES_END +%state ST_JSP_VBL_DQUOTES_END + +%state ST_JSP_DQUOTED_VBL +%state ST_JSP_SQUOTED_VBL + +// Letter = ([A-Za-z]) +// Digit = ([0-9]) + +/** + * smaller tokens + */ +genericTagOpen = < +genericTagClose = > +genericEndTagOpen = <\/ +genericEmptyTagClose = \/> + +PIstart = <\? +PIend = \?> + + +// [1] document ::= prolog element Misc* +document = ({prolog} {element} {Misc}*) + +// [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + //Char = (.) +Char = [\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD] + +// [3] S ::= (0x20 | 0x9 | 0xD | 0xA)+ +S = [\x20\x09\x0D\x0A]+ + +// [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender +NameChar = ({Letter} | {Digit} | \. | \- | _ | : | {CombiningChar} | {Extender}) + +// [5] Name ::= (Letter | '_' | ':') NameChar* +//Name = ({NameChar}{NameChar}*) +Name = ({Letter} | _ | :){NameChar}* + +// [6] Names ::= {Name} ({S} {Name})* +Names = ({Name} ({S} {Name})*) + +// [7] Nmtoken ::= (NameChar)+ +Nmtoken = ({NameChar}+) + +// [8] Nmtokens ::= Nmtoken (S Nmtoken)* +Nmtokens = ({Nmtoken} ({S} {Nmtoken})*) + +// [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | "'" ([^%&'] | PEReference | Reference)* "'" +EntityValue = (\" ([^%&\"] | {PEReference} | {Reference})* \" | \' ([^%&\'] | {PEReference} | {Reference})* \') + +// \x24 = '$', \x7b = '{', \x23 = '#' +// [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" +AttValue = ( \"([^<"\x24\x23] | [\x24\x23][^\x7b\x3c"] | \\[\x24\x23][\x7b] | {Reference})* [\x24\x23]*\" | \'([^<'\x24\x23] | [\x24\x23][^\x7b\x3c'] | \\[\x24\x23][\x7b] | {Reference})*[\x24\x23]*\' | ([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )*) + +// As Attvalue, but accepts escaped versions of the lead-in quote also +QuotedAttValue = ( \"([^<"\x24\x23] | [\x24\x23][^\x7b"] | \\[\x24\x23][\x7b] | \\\" | {Reference})*[\x24\x23]*\" | \'([^<'\x24\x23] | [\x24\x23][^\x7b'] | \\[\x24\x23][\x7b] | \\\' | {Reference})*[\x24\x23]*\' | ([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )*) + +// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") +SystemLiteral = ((\" [^\"]* \") | (\' [^\']* \')) + +// [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" +PubidLiteral = (\" {PubidChar}* \" | \' ({PubidChar}\')* "'") + +// [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] +PubidChar = ([\040\015\012] | [a-zA-Z0-9] | [\-\'()\+,.\/:=?;!\*#@\$_%]) + +// [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) +// implement lookahead behavior during action definition +CharData = ([^<&(\]\]>)]*) + +// [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' +CommentStart = (<!\-\-) +CommentEnd = (\-\->) +Comment = ({CommentStart}.*{CommentEnd}) + +// [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' +PI = (<\?{PITarget} {Char}* \?>) + +// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) +PITarget = ({Name}((X|x)(M|m)(L|l))) + +// [18] CDSect ::= CDStart CData CDEnd +CDSect = ({CDStart}{CData}{CDEnd}) + +// [19] CDStart ::= '<![CDATA[' +CDStart = <!\[CDATA\[ + +// [20] CData ::= (Char* - (Char* ']]>' Char*)) +// implement lookahead behavior during action definition +CData = ([^(\]\]>)]*) + +// [21] CDEnd ::= ']]>' +CDEnd = (\]\]>) + +// [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? +prolog = ({XMLDecl}? {Misc}* ({doctypedecl} {Misc}*)?) + +// [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' +XMLDecl = (<\?xml {VersionInfo} {EncodingDecl}? {SDDecl}? {S}? \?>) + +// [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ") +VersionInfo = ({S}version{Eq}(\'{VersionNum}\' | \"{VersionNum}\")) + +// [25] Eq ::= S? '=' S? +Eq = (\=) + +// [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+ +VersionNum = (([a-zA-Z0-9_.:]|\-)+) + +// [27] Misc ::= Comment | PI | S +Misc = ({Comment} | {PI} | {S}) + +// [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' (markupdecl | PEReference | S)* ']' S?)? '>' +doctypedecl = (<\!DOCTYPE{S}{Name} ({S}{ExternalID})? {S}? (\[ ({markupdecl}|{PEReference}|{S})* \]{S}?)?>) + +// [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment +markupdecl = ({elementdecl} | {AttlistDecl} | {EntityDecl} | {NotationDecl} | {PI} | {Comment}) + +// [30] extSubset ::= TextDecl? extSubsetDecl +extSubset = ({TextDecl}? {extSubsetDecl}) + +// [31] extSubsetDecl ::= ( markupdecl | conditionalSect | PEReference | S )* +extSubsetDecl = (( {markupdecl} | {conditionalSect} | {PEReference} | {S} )*) + +// [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) +SDDecl = ({S}standalone{Eq}{S}*((\'(yes|no)\')|(\"(yes|no)\"))) + +// [33] LanguageID ::= Langcode ('-' Subcode)* +LanguageID = ({Langcode}(\-{Subcode})*) + +// [34] Langcode ::= ISO639Code | IanaCode | UserCode +Langcode = ({ISO639Code} | {IanaCode} | {UserCode}) + +// [35] ISO639Code ::= ([a-z] | [A-Z]) ([a-z] | [A-Z]) +ISO639Code = (([a-z]|[A-Z])([a-z]|[A-Z])) + +// [36] IanaCode ::= ('i' | 'I') '-' ([a-z] | [A-Z])+ +IanaCode = ((i|I)\-([a-z]|[A-Z])+) + +// [37] UserCode ::= ('x' | 'X') '-' ([a-z] | [A-Z])+ +UserCode = ((x|X)\-([a-z]|[A-Z])+) + +// [38] Subcode ::= ([a-z] | [A-Z])+ +Subcode = (([a-z]|[A-Z])+) + +// [39] element ::= EmptyElemTag | STag content ETag +element = ({EmptyElemTag} | {STag} {content} {ETag}) + +// [40] STag ::= '<' Name (S Attribute)* S? '>' +STag = (<{Name}({S}{Attribute})*{S}?>) + +// [41] Attribute ::= Name Eq AttValue +Attribute = ({Name}{S}*{Eq}{S}*{AttValue}) + +// [42] ETag ::= 'Name S? '>' +ETag = (<\/{Name}{S}?>) + +// [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)* +content = (({element} | {CharData} | {Reference} | {CDSect} | {PI} | {Comment})*) + +// [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' +EmptyElemTag = (<{Name}({S}{Attribute})*{S}?\/>) + +// [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>' +elementdecl = (<\!ELEMENT{S}{Name}{S}{contentspec}{S}?>) + +// [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children +contentspec = (EMPTY|ANY|{Mixed}|{children}) + +// [47] children ::= (choice | seq) ('?' | '*' | '+')? +children = (({choice}|{seq})(\?|\*|\+)?) + +// CAUSES LOOP THROUGH DEFS OF CHOICE AND SEQ +// [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')? +cp = (({Name} | {choice} | {seq}) (\?|\*|\+)?) + +// [49] choice ::= '(' S? cp ( S? '|' S? cp )* S? ')' +// choice = \({S}?{cp}({S}?\|{S}?{cp})*{S}?\) +choice = \({S}?{Name}({S}?\|{S}?{Name})*{S}?\) + +// [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' +// seq = (\({S}?{cp}({S}?\,{S}?{cp})*{S}?\)) +seq = (\({S}?{Name}({S}?\,{S}?{Name})*{S}?\)) + +// [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' +Mixed = ({S}?\#PCDATA({S}?\|{S}?{Name})*{S}?)*\|({S}?\#PCDATA{S}?) + +// [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>' +AttlistDecl = (<\!ATTLIST{S}{Name}{AttDef}*{S}?>) + +// [53] AttDef ::= S Name S AttType S DefaultDecl +AttDef = ({S}{Name}{S}{AttType}{S}{DefaultDecl}) + +// [54] AttType ::= StringType | TokenizedType | EnumeratedType +AttType = ({StringType} | {TokenizedType} | {EnumeratedType}) + +// [55] StringType ::= 'CDATA' +StringType = (CDATA) + +// [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS' +TokenizedType = (ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS) + +// [57] EnumeratedType ::= NotationType | Enumeration +EnumeratedType = ({NotationType} | {Enumeration}) + +// [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' +NotationType = (NOTATION{S}\({S}?{Name}({S}?\|{S}?{Name})*{S}?\)) + +// [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' +Enumeration = (\({S}?{Nmtoken}({S}?\|{S}?{Nmtoken})*{S}?\)) + +// [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) +DefaultDecl = (\#REQUIRED|\#IMPLIED|((\#FIXED{S})?{AttValue})) + +// [61] conditionalSect ::= includeSect | ignoreSect +conditionalSect = ({includeSect} | {ignoreSect}) + +// [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>' +includeSect = (<\!\[{S}?INCLUDE{S}?\[{extSubsetDecl}\]\]>) + +// [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>' +ignoreSect = (<\!\[{S}?IGNORE{S}?\[{ignoreSectContents}*\]\]>) + +// [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)* +ignoreSectContents = ({Ignore}(<\!\[{ignoreSectContents}\]\]>{Ignore})*) + +// [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*) +Ignore = ([^(\<\!\[|\]\]\>)]*) + +// [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';' +CharRef = (&#[0-9]+;|&#x[0-9a-fA-F]+;) + +// [67] Reference ::= EntityRef | CharRef +Reference = ({EntityRef} | {CharRef}) + +// [68] EntityRef = '&' Name ';' +EntityRef = (&{Name};) + +// [69] PEReference ::= '%' Name ';' +PEReference = (%{Name};) + +// [70] EntityDecl ::= GEDecl | PEDecl +EntityDecl = ({GEDecl} | {PEDecl}) + +// [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>' +GEDecl = (<\!ENTITY{S}{Name}{S}{EntityDef}{S}?>) + +// [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>' +PEDecl = (<\!ENTITY{S}\%{S}{Name}{S}{PEDef}{S}?>) + +// [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) +EntityDef = ({EntityValue} | ({ExternalID}{NDataDecl}?)) + +// [74] PEDef ::= EntityValue | ExternalID +PEDef = ({EntityValue} | {ExternalID}) + +// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral +ExternalID = (SYSTEM{S}{SystemLiteral}|PUBLIC{S}{PubidLiteral}{S}{SystemLiteral} ) + +// [76] NDataDecl ::= S 'NDATA' S Name +NDataDecl = ({S}NDATA{S}{Name}) + +// [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>' +TextDecl = (<\?xml{VersionInfo}?{EncodingDecl}{S}?\?>) + +// [78] extParsedEnt ::= TextDecl? content +extParsedEnt = ({TextDecl}?{content}) + +// [79] extPE ::= TextDecl? extSubsetDecl +extPE = ({TextDecl}?{extSubsetDecl}) + +// [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) +EncodingDecl = ({S}encoding{S}*{Eq}{S}*(\"{EncName}\"|\'{EncName}\')) + +// [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* +EncName = ([A-Za-z]([A-Za-z0-9._]|\-)*) + +// [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>' +NotationDecl = (<\!NOTATION{S}{Name}{S}({ExternalID}|{PublicID}){S}?>) + +// [83] PublicID ::= 'PUBLIC' S PubidLiteral +PublicID = (PUBLIC{S}{PubidLiteral}) + +// [84] Letter ::= BaseChar | Ideographic +Letter = ({BaseChar} | {Ideographic}) + +// [85] BaseChar ::= [#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] +// | [#x00D8-#x00F6] | [#x00F8-#x00FF] | [#x0100-#x0131] +// | [#x0134-#x013E] | [#x0141-#x0148] | [#x014A-#x017E] +// | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] +// | [#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 +// | [#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] +// | [#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 +// | [#x03E2-#x03F3] | [#x0401-#x040C] | [#x040E-#x044F] +// | [#x0451-#x045C] | [#x045E-#x0481] | [#x0490-#x04C4] +// | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] +// | [#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 +// | [#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] +// | [#x0621-#x063A] | [#x0641-#x064A] | [#x0671-#x06B7] +// | [#x06BA-#x06BE] | [#x06C0-#x06CE] | [#x06D0-#x06D3] | #x06D5 +// | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | [#x0958-#x0961] +// | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] +// | [#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] +// | [#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] +// | [#x0A0F-#x0A10] | [#x0A13-#x0A28] | [#x0A2A-#x0A30] +// | [#x0A32-#x0A33] | [#x0A35-#x0A36] | [#x0A38-#x0A39] +// | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | [#x0A85-#x0A8B] +// | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | [#x0AAA-#x0AB0] +// | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 +// | [#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] +// | [#x0B2A-#x0B30] | [#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D +// | [#x0B5C-#x0B5D] | [#x0B5F-#x0B61] | [#x0B85-#x0B8A] +// | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | [#x0B99-#x0B9A] | #x0B9C +// | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | [#x0BA8-#x0BAA] +// | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] +// | [#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] +// | [#x0C35-#x0C39] | [#x0C60-#x0C61] | [#x0C85-#x0C8C] +// | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] | [#x0CAA-#x0CB3] +// | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | [#x0D05-#x0D0C] +// | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] +// | [#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] +// | [#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | [#x0E87-#x0E88] +// | #x0E8A | #x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] +// | [#x0EA1-#x0EA3] | #x0EA5 | #x0EA7 | [#x0EAA-#x0EAB] +// | [#x0EAD-#x0EAE] | #x0EB0 | [#x0EB2-#x0EB3] | #x0EBD +// | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] +// | [#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] +// | [#x1105-#x1107] | #x1109 | [#x110B-#x110C] | [#x110E-#x1112] +// | #x113C | #x113E | #x1140 | #x114C | #x114E | #x1150 +// | [#x1154-#x1155] | #x1159 | [#x115F-#x1161] | #x1163 | #x1165 +// | #x1167 | #x1169 | [#x116D-#x116E] | [#x1172-#x1173] | #x1175 +// | #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | [#x11B7-#x11B8] +// | #x11BA | [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 +// | [#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] +// | [#x1F18-#x1F1D] | [#x1F20-#x1F45] | [#x1F48-#x1F4D] +// | [#x1F50-#x1F57] | #x1F59 | #x1F5B | #x1F5D | [#x1F5F-#x1F7D] +// | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | [#x1FC2-#x1FC4] +// | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] +// | [#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 +// | [#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] +// | [#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3] +BaseChar = [\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3] + +// [86] Ideographic ::= [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029] +Ideographic = [\u4E00-\u9FA5\u3007\u3021-\u3029] + +// [87] CombiningChar ::= [#x0300-#x0345] | [#x0360-#x0361] +// | [#x0483-#x0486] | [#x0591-#x05A1] | [#x05A3-#x05B9] +// | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 +// | [#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] +// | [#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] +// | [#x0901-#x0903] | #x093C | [#x093E-#x094C] | #x094D +// | [#x0951-#x0954] | [#x0962-#x0963] | [#x0981-#x0983] | #x09BC +// | #x09BE | #x09BF | [#x09C0-#x09C4] | [#x09C7-#x09C8] +// | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | #x0A3C +// | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] +// | [#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC +// | [#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] +// | [#x0B01-#x0B03] | #x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] +// | [#x0B4B-#x0B4D] | [#x0B56-#x0B57] | [#x0B82-#x0B83] +// | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | [#x0BCA-#x0BCD] | #x0BD7 +// | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | [#x0C46-#x0C48] +// | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] +// | [#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] +// | [#x0CD5-#x0CD6] | [#x0D02-#x0D03] | [#x0D3E-#x0D43] +// | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | #x0D57 | #x0E31 +// | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | [#x0EB4-#x0EB9] +// | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | #x0F35 +// | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] +// | [#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] +// | [#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 +// | [#x302A-#x302F] | #x3099 | #x309A +CombiningChar = [\u0300-\u0345\u0360-\u0361\u0483-\u0486\u0591-\u05A1\u05A3-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4\u064B-\u0652\u0670\u06D6-\u06DC\u06DD-\u06DF\u06E0-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0901-\u0903\u093C\u093E-\u094C\u094D\u0951-\u0954\u0962-\u0963\u0981-\u0983\u09BC\u09BE\u09BF\u09C0-\u09C4\u09C7-\u09C8\u09CB-\u09CD\u09D7\u09E2-\u09E3\u0A02\u0A3C\u0A3E\u0A3F\u0A40-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A70-\u0A71\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0B01-\u0B03\u0B3C\u0B3E-\u0B43\u0B47-\u0B48\u0B4B-\u0B4D\u0B56-\u0B57\u0B82-\u0B83\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C82-\u0C83\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5-\u0CD6\u0D02-\u0D03\u0D3E-\u0D43\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86-\u0F8B\u0F90-\u0F95\u0F97\u0F99-\u0FAD\u0FB1-\u0FB7\u0FB9\u20D0-\u20DC\u20E1\u302A-\u302F\u3099\u309A] + +// [88] Digit ::= [#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] +// | [#x0966-#x096F] | [#x09E6-#x09EF] | [#x0A66-#x0A6F] +// | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | [#x0BE7-#x0BEF] +// | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] +// | [#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29] +Digit = [\u0030-\u0039\u0660-\u0669\u06F0-\u06F9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE7-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29] + +// [89] Extender ::= #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 +// | #x0EC6 | #x3005 | [#x3031-#x3035] | [#x309D-#x309E] +// | [#x30FC-#x30FE] +Extender = [\u00B7\u02D0\u02D1\u0387\u0640\u0E46\u0EC6\u3005\u3031-\u3035\u309D-\u309E\u30FC-\u30FE] + + + +/** + * JSP and scripting marker allowances + */ + +jspCommentStart = <%-- +jspCommentEnd = --%> + +jspScriptletStart = <% +jspExpressionStart = {jspScriptletStart}= +jspDeclarationStart = {jspScriptletStart}\! +jspScriptletEnd = %> +jspDirectiveStart = {jspScriptletStart}@ + +%% + +/* white space within a tag */ +<ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE, ST_PI, ST_XML_PI_EQUALS, ST_XML_PI_ATTRIBUTE_NAME, ST_XML_PI_ATTRIBUTE_VALUE, ST_XML_DECLARATION, ST_XML_DOCTYPE_DECLARATION, ST_XML_ELEMENT_DECLARATION, ST_XML_ATTLIST_DECLARATION, ST_XML_DECLARATION_CLOSE, ST_XML_DOCTYPE_ID_PUBLIC, ST_XML_DOCTYPE_ID_SYSTEM, ST_XML_DOCTYPE_EXTERNAL_ID, ST_JSP_DIRECTIVE_NAME, ST_JSP_DIRECTIVE_ATTRIBUTE_NAME, ST_JSP_DIRECTIVE_EQUALS, ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE,ST_XML_ATTRIBUTE_VALUE_DQUOTED,ST_XML_ATTRIBUTE_VALUE_SQUOTED,ST_DHTML_ATTRIBUTE_NAME,ST_DHTML_EQUALS,ST_DHTML_ATTRIBUTE_VALUE,ST_DHTML_TAG_CLOSE> {S}* { + if(Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + return WHITE_SPACE; +} + +// BEGIN REGULAR XML +/* handle opening a new tag almost anywhere */ + + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_DECLARATION, ST_JSP_DIRECTIVE_NAME, ST_JSP_DIRECTIVE_NAME_WHITESPACE, ST_JSP_DIRECTIVE_ATTRIBUTE_NAME, ST_JSP_DIRECTIVE_EQUALS, ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE> {genericTagOpen} { + if(Debug.debugTokenizer) + dump("\nstart tag open");//$NON-NLS-1$ + if (!fStateStack.empty() && fStateStack.peek()== ST_XML_COMMENT){ + fStateStack.pop(); + fEmbeddedHint = XML_COMMENT_TEXT; + yybegin(ST_XML_TAG_NAME); + String tagName = ""; + assembleEmbeddedTagSequence(XML_TAG_OPEN, tagName); // ? + return PROXY_CONTEXT; + } + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; +} +/* VERY special cases for tags as values */ +/* quoted JSP */ +<ST_XML_ATTRIBUTE_VALUE_DQUOTED> ["] { + return isJspTag()? JSP_TAG_ATTRIBUTE_VALUE_DQUOTE : XML_TAG_ATTRIBUTE_VALUE_DQUOTE; +} +<ST_XML_ATTRIBUTE_VALUE_SQUOTED> ['] { + return isJspTag() ? JSP_TAG_ATTRIBUTE_VALUE_SQUOTE : XML_TAG_ATTRIBUTE_VALUE_SQUOTE; +} +<ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> ["] { + String type = yy_lexical_state == ST_XML_ATTRIBUTE_VALUE ? XML_TAG_ATTRIBUTE_VALUE_DQUOTE : JSP_TAG_ATTRIBUTE_VALUE_DQUOTE; + + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", "+type);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + yybegin(ST_XML_ATTRIBUTE_VALUE_DQUOTED); + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("JSP attribute value start - complex double quoted");//$NON-NLS-1$ + assembleEmbeddedContainer(type, type); + fStateStack.pop(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + if (fEmbeddedContainer.getLastRegion().getType() == UNDEFINED) { + fProxyUnknownRegion = fRegionFactory.createToken(XML_TAG_ATTRIBUTE_VALUE, fEmbeddedContainer.getStart(), fEmbeddedContainer.getTextLength(), fEmbeddedContainer.getLength()); + return PROXY_UNKNOWN_CONTEXT; + } + return PROXY_CONTEXT; +} +<ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> ['] { + String type = yy_lexical_state == ST_XML_ATTRIBUTE_VALUE ? XML_TAG_ATTRIBUTE_VALUE_SQUOTE : JSP_TAG_ATTRIBUTE_VALUE_SQUOTE; + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", "+type);//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + yybegin(ST_XML_ATTRIBUTE_VALUE_SQUOTED); + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("JSP attribute value start - complex single quoted");//$NON-NLS-1$ + assembleEmbeddedContainer(type, type); + fStateStack.pop(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + if (fEmbeddedContainer.getLastRegion().getType() == UNDEFINED) { + fProxyUnknownRegion = fRegionFactory.createToken(XML_TAG_ATTRIBUTE_VALUE, fEmbeddedContainer.getStart(), fEmbeddedContainer.getTextLength(), fEmbeddedContainer.getLength()); + return PROXY_UNKNOWN_CONTEXT; + } + return PROXY_CONTEXT; +} + +<ST_XML_ATTRIBUTE_VALUE_DQUOTED> ([^<"\x24\x23]+|[\x24\x23]{S}*) +{ + return XML_TAG_ATTRIBUTE_VALUE; +} +<ST_XML_ATTRIBUTE_VALUE_SQUOTED> ([^<'\x24\x23]+|[\x24\x23]{S}*) +{ + return XML_TAG_ATTRIBUTE_VALUE; +} + + +<ST_XML_ATTRIBUTE_VALUE_DQUOTED,ST_XML_ATTRIBUTE_VALUE_SQUOTED> {genericTagOpen} { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", genericTagOpen");//$NON-NLS-1$ + } + int incomingState = yystate(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + if(Debug.debugTokenizer) + dump("tag inside of JSP attribute value start");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedContainer(XML_TAG_OPEN, new String[]{XML_TAG_CLOSE,XML_EMPTY_TAG_CLOSE}); + if(yystate() != ST_ABORT_EMBEDDED) + yybegin(incomingState); + return PROXY_CONTEXT; +} +<ST_XML_ATTRIBUTE_VALUE_DQUOTED,ST_XML_ATTRIBUTE_VALUE_SQUOTED> {genericEndTagOpen} { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", genericEndTagOpen");//$NON-NLS-1$ + } + int incomingState = yystate(); + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + if(Debug.debugTokenizer) + dump("JSP attribute value start - end tag");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedContainer(XML_END_TAG_OPEN, new String[]{XML_TAG_CLOSE,XML_EMPTY_TAG_CLOSE}); + if(yystate() != ST_ABORT_EMBEDDED) + yybegin(incomingState); + return PROXY_CONTEXT; +} + +/* unquoted */ +<ST_XML_ATTRIBUTE_VALUE> {genericTagOpen} { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", unquoted genericTagOpen");//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("JSP tag embedded name start - start tag");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedContainer(XML_TAG_OPEN, new String[]{XML_TAG_CLOSE,XML_EMPTY_TAG_CLOSE}); + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + return PROXY_CONTEXT; +} + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE, ST_XML_DECLARATION> {genericEndTagOpen} { + if(Debug.debugTokenizer) + dump("\nend tag open");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_TAG_NAME); + return XML_END_TAG_OPEN; +} +/* specially treated JSP tag names */ +<ST_XML_TAG_NAME> jsp:root { + if(Debug.debugTokenizer) + dump("jsp:root tag name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return JSP_ROOT_TAG_NAME; +} +<ST_XML_TAG_NAME> jsp:directive.(page|include|tag|taglib|attribute|variable) { + if(Debug.debugTokenizer) + dump("jsp directive tag name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return JSP_DIRECTIVE_NAME; +} +/* the tag's name was found, start scanning for attributes */ +<ST_XML_TAG_NAME> {Name} { + if(Debug.debugTokenizer) + dump("tag name");//$NON-NLS-1$ + String tagname = yytext(); + boolean jspTag = tagname.indexOf(':') != -1; + if (fEmbeddedContainer != null) { + fEmbeddedTag = jspTag; + fInTagEmbedded = true; + } + else { + fContainerTag = jspTag; + fInTagContainer = true; + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} + +/* another attribute name was found, resume looking for the equals sign */ +<ST_XML_ATTRIBUTE_NAME, ST_XML_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +/* an equal sign was found, what's next is the value */ +<ST_XML_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(isJspTag() ? ST_JSP_ATTRIBUTE_VALUE : ST_XML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +/* the value was found, look for the next name */ +<ST_XML_ATTRIBUTE_VALUE> {AttValue} { /* only allow for non-JSP tags for this does not obey JSP quoting rules */ + if(Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} +<ST_JSP_ATTRIBUTE_VALUE> {QuotedAttValue} { /* JSP attribute values have escape semantics */ + if(Debug.debugTokenizer) + dump("jsp attr value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} + +/* the tag's close was found */ +<ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> {genericTagClose} { + if(Debug.debugTokenizer) + dump("tag close");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + if(isBlockMarker()) { + fEmbeddedHint = getBlockMarkerContext(); + fEmbeddedPostState = ST_BLOCK_TAG_SCAN; + yybegin(ST_BLOCK_TAG_SCAN); + } + else + yybegin(YYINITIAL); + + if (fEmbeddedContainer != null) + fInTagEmbedded = false; + else + fInTagContainer = false; + + return XML_TAG_CLOSE; +} +/* the tag's close was found, but the tag doesn't need a matching end tag */ +<ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> {genericEmptyTagClose} { + yybegin(YYINITIAL); + fEmbeddedHint = UNDEFINED; + if(Debug.debugTokenizer) + dump("empty tag close");//$NON-NLS-1$ + + if (fEmbeddedContainer != null) + fInTagEmbedded = false; + else + fInTagContainer = false; + + return XML_EMPTY_TAG_CLOSE; +} + +<ST_XML_TAG_NAME> [^</>\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3\u4E00-\u9FA5\u3007\u3021-\u3029]* { + if(Debug.debugTokenizer) + dump("inappropriate tag name");//$NON-NLS-1$ + if(!fStateStack.empty() && (fStateStack.peek()==ST_XML_ATTRIBUTE_VALUE_SQUOTED||fStateStack.peek()==ST_XML_ATTRIBUTE_VALUE_DQUOTED)) { + yybegin(ST_ABORT_EMBEDDED); + char c = yy_buffer[yy_markedPos - 1]; + if (fStateStack.peek()==ST_XML_ATTRIBUTE_VALUE_DQUOTED && c == '\"') { + return isJspTag() ? JSP_TAG_ATTRIBUTE_VALUE_DQUOTE : XML_TAG_ATTRIBUTE_VALUE_DQUOTE; + } + if (fStateStack.peek()==ST_XML_ATTRIBUTE_VALUE_SQUOTED && c == '\'') { + return isJspTag() ? JSP_TAG_ATTRIBUTE_VALUE_SQUOTE : XML_TAG_ATTRIBUTE_VALUE_SQUOTE; + } + yypushback(yylength()-1); + return XML_TAG_ATTRIBUTE_VALUE; + } + yybegin(YYINITIAL); + return XML_CONTENT; +} + +// END REGULAR XML + +// BEGIN NESTED XML TAGS +<ST_XML_ATTRIBUTE_NAME, ST_XML_EQUALS> <{Name} { + String tagName = yytext().substring(1); + // pushback to just after the opening bracket + yypushback(yylength() - 1); + /* + * If this tag can not be nested or we're already searching for an + * attribute name, equals, or value, return immediately. + */ + if (!isNestable(tagName) || (!fStateStack.empty() && (fStateStack.peek() == ST_XML_ATTRIBUTE_NAME || fStateStack.peek() == ST_XML_EQUALS || fStateStack.peek() == ST_XML_ATTRIBUTE_VALUE || fStateStack.peek() == ST_JSP_ATTRIBUTE_VALUE))) { + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + if(Debug.debugTokenizer) + dump("tag in place of attr name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + // embedded container should be looking for the name (again) next + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedTagSequence(XML_TAG_OPEN, tagName); // ? + fStateStack.pop(); + yybegin(ST_XML_EQUALS); + return PROXY_CONTEXT; +} +<ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> <{Name} { + String tagName = yytext().substring(1); + // pushback to just after the opening bracket + yypushback(yylength() - 1); + /* + * If this tag can not be nested or we're already searching for an + * attribute name, equals, or value, return immediately. + */ + if (!isNestable(tagName) || (!fStateStack.empty() && (fStateStack.peek() == ST_XML_ATTRIBUTE_NAME || fStateStack.peek() == ST_XML_EQUALS || fStateStack.peek() == ST_XML_ATTRIBUTE_VALUE || fStateStack.peek() == ST_JSP_ATTRIBUTE_VALUE))) { + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + if(Debug.debugTokenizer) + dump("tag in place of attr value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + // embedded container should be looking for the name (again) next + yybegin(ST_XML_TAG_NAME); + assembleEmbeddedTagSequence(XML_TAG_OPEN, tagName); // ? + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + return PROXY_CONTEXT; +} +// END NESTED XML + +// XML & JSP Comments + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE, ST_XML_DECLARATION, ST_JSP_DIRECTIVE_NAME, ST_JSP_DIRECTIVE_NAME_WHITESPACE, ST_JSP_DIRECTIVE_ATTRIBUTE_NAME, ST_JSP_DIRECTIVE_EQUALS, ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE> {CommentStart} { + if(Debug.debugTokenizer) + dump("\ncomment start");//$NON-NLS-1$ + fEmbeddedHint = XML_COMMENT_TEXT; + fEmbeddedPostState = ST_XML_COMMENT; + yybegin(ST_XML_COMMENT); + return XML_COMMENT_OPEN; +} +<ST_XML_COMMENT> .|\r|\n { + if(Debug.debugTokenizer) + dump("comment content");//$NON-NLS-1$ + return scanXMLCommentText(); +} + +<ST_XML_COMMENT_END> {CommentEnd} { + if(Debug.debugTokenizer) + dump("comment end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_COMMENT_CLOSE; +} + +<ST_JSP_CONTENT> {jspScriptletEnd} { + if(Debug.debugTokenizer) + dump("JSP end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return JSP_CLOSE; +} +<ST_JSP_CONTENT> .|\n|\r { + if(Debug.debugTokenizer) + dump("JSP code content");//$NON-NLS-1$ + return doScan("%>", false, false, false, JSP_CONTENT, ST_JSP_CONTENT, ST_JSP_CONTENT); +} +{jspScriptletStart} { + /* JSP scriptlet begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(1); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(2); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(2); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(2); + return scanJSPCommentText(); + } + // finished sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just a regular scriptlet out in content + if(Debug.debugTokenizer) + dump("\nJSP scriptlet start");//$NON-NLS-1$ + yybegin(ST_JSP_CONTENT); + return JSP_SCRIPTLET_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", jspScriptletStart");//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP scriptlet start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_CONTENT); + assembleEmbeddedContainer(JSP_SCRIPTLET_OPEN, JSP_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if(yystate() == ST_JSP_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + + return PROXY_CONTEXT; + } +} +{jspExpressionStart} { + /* JSP expression begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(2); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(3); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(3); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(3); + return scanJSPCommentText(); + } + // end sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just an expression out in content + if(Debug.debugTokenizer) + dump("\nJSP expression start");//$NON-NLS-1$ + yybegin(ST_JSP_CONTENT); + return JSP_EXPRESSION_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", jspExpressionStart");//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP expression start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_CONTENT); + assembleEmbeddedContainer(JSP_EXPRESSION_OPEN, JSP_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if(yystate() == ST_JSP_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + + return PROXY_CONTEXT; + } +} +{jspDeclarationStart} { + /* JSP declaration begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(2); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(3); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(3); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(3); + return scanJSPCommentText(); + } + // end sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just a declaration out in content + if(Debug.debugTokenizer) + dump("\nJSP declaration start");//$NON-NLS-1$ + yybegin(ST_JSP_CONTENT); + return JSP_DECLARATION_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", jspDeclarationStart");//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP declaration start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_CONTENT); + assembleEmbeddedContainer(JSP_DECLARATION_OPEN, JSP_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if(yystate() == ST_JSP_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } +} +{jspCommentStart} { + /* JSP comment begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(3); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(4); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(4); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(4); + return scanJSPCommentText(); + } + else if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_JSP_COMMENT); + assembleEmbeddedContainer(JSP_COMMENT_OPEN, JSP_COMMENT_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) + yybegin(ST_BLOCK_TAG_SCAN); + return PROXY_CONTEXT; + } + // finished sanity checks + if(yystate()==YYINITIAL) { + // the simple case, just a regular scriptlet out in content + if(Debug.debugTokenizer) + dump("\nJSP comment start");//$NON-NLS-1$ + yybegin(ST_JSP_COMMENT); + return JSP_COMMENT_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", jspCommentStart");//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP comment start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_COMMENT); + // the comment container itself will act as comment text + fEmbeddedHint = JSP_COMMENT_TEXT; + assembleEmbeddedContainer(JSP_COMMENT_OPEN, JSP_COMMENT_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + /* + * required help for successive embedded regions; mark this one as a + * comment so it will be otherwise ignored but preserved (which is why + * we can't use white-space) + */ + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if(yystate() == ST_JSP_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } +} + +<ST_BLOCK_TAG_INTERNAL_SCAN> {jspCommentStart} { + yybegin(ST_JSP_COMMENT); + assembleEmbeddedContainer(JSP_COMMENT_OPEN, JSP_COMMENT_CLOSE); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) + yybegin(ST_BLOCK_TAG_SCAN); + return PROXY_CONTEXT; +} + +{jspDirectiveStart} { + /* JSP directive begun (anywhere) + * A consequence of the start anywhere possibility is that the + * incoming state must be checked to see if it's erroneous + * due to the order of precedence generated + */ + // begin sanity checks + if(yystate() == ST_JSP_CONTENT) { + // at the beginning?! + yypushback(2); + return JSP_CONTENT; + } + else if(yystate() == ST_BLOCK_TAG_SCAN) { + yypushback(3); + return doBlockTagScan(); + } + else if(yystate() == ST_XML_COMMENT) { + yypushback(3); + return scanXMLCommentText(); + } + else if(yystate() == ST_JSP_COMMENT) { + yypushback(3); + return scanJSPCommentText(); + } + // end sanity checks + fStateStack.push(yystate()); + if(fStateStack.peek()==YYINITIAL) { + // the simple case, just a declaration out in content + if(Debug.debugTokenizer) + dump("\nJSP directive start");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_NAME); + return JSP_DIRECTIVE_OPEN; + } + else { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", jspDirectiveStart");//$NON-NLS-1$ + } + if(Debug.debugTokenizer) + dump("JSP declaration start");//$NON-NLS-1$ + if(yystate() == ST_XML_ATTRIBUTE_VALUE_DQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_DQUOTED; + else if(yystate() == ST_XML_ATTRIBUTE_VALUE_SQUOTED) + fEmbeddedPostState = ST_XML_ATTRIBUTE_VALUE_SQUOTED; + else if(yystate() == ST_CDATA_TEXT) { + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + } + yybegin(ST_JSP_DIRECTIVE_NAME); + assembleEmbeddedContainer(JSP_DIRECTIVE_OPEN, new String[]{JSP_DIRECTIVE_CLOSE, JSP_CLOSE}); + if(yystate() == ST_BLOCK_TAG_INTERNAL_SCAN) { + yybegin(ST_BLOCK_TAG_SCAN); + return BLOCK_TEXT; + } + // required help for successive embedded regions + if(yystate() == ST_XML_TAG_NAME) { + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else if((yystate() == ST_XML_ATTRIBUTE_NAME || yystate() == ST_XML_EQUALS)) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + } + else if(yystate() == ST_XML_ATTRIBUTE_VALUE) { + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + return PROXY_CONTEXT; + } +} + + +<ST_JSP_DIRECTIVE_NAME> {Name} { + if(Debug.debugTokenizer) + dump("JSP directive name");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_NAME_WHITESPACE); + return JSP_DIRECTIVE_NAME; +} +<ST_JSP_DIRECTIVE_NAME_WHITESPACE> {S}? { + if(Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_ATTRIBUTE_NAME); + return WHITE_SPACE; +} +<ST_JSP_DIRECTIVE_ATTRIBUTE_NAME, ST_JSP_DIRECTIVE_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +<ST_JSP_DIRECTIVE_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +<ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE> {AttValue} { + if(Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + yybegin(ST_JSP_DIRECTIVE_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} +<ST_JSP_DIRECTIVE_NAME, ST_JSP_DIRECTIVE_NAME_WHITESPACE, ST_JSP_DIRECTIVE_ATTRIBUTE_NAME, ST_JSP_DIRECTIVE_EQUALS, ST_JSP_DIRECTIVE_ATTRIBUTE_VALUE> {jspScriptletEnd} { + if(Debug.debugTokenizer) + dump("JSP end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return JSP_DIRECTIVE_CLOSE; +} + +<YYINITIAL> {jspCommentStart} { + if(Debug.debugTokenizer) + dump("\nJSP comment start");//$NON-NLS-1$ + yybegin(ST_JSP_COMMENT); + return JSP_COMMENT_OPEN; +} + +<ST_XML_ATTRIBUTE_VALUE_DQUOTED> \x24\x7b { + int enterState = yystate(); + yybegin(ST_JSP_DQUOTED_EL); + assembleEmbeddedContainer(JSP_EL_OPEN, new String[]{JSP_EL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_DQUOTE, JSP_TAG_ATTRIBUTE_VALUE_DQUOTE}); + // abort early when an unescaped double quote is found in the EL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_DQUOTE) || fEmbeddedContainer.getLastRegion().getType().equals(JSP_TAG_ATTRIBUTE_VALUE_DQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; +} +<ST_XML_ATTRIBUTE_VALUE_DQUOTED> \x23\x7b { + int enterState = yystate(); + yybegin(ST_JSP_DQUOTED_VBL); + assembleEmbeddedContainer(JSP_VBL_OPEN, new String[]{JSP_VBL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_DQUOTE, JSP_TAG_ATTRIBUTE_VALUE_DQUOTE}); + // abort early when an unescaped double quote is found in the VBL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_DQUOTE) || fEmbeddedContainer.getLastRegion().getType().equals(JSP_TAG_ATTRIBUTE_VALUE_DQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; +} +<ST_XML_ATTRIBUTE_VALUE_SQUOTED> \x24\x7b { + int enterState = yystate(); + yybegin(ST_JSP_SQUOTED_EL); + assembleEmbeddedContainer(JSP_EL_OPEN, new String[]{JSP_EL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_SQUOTE, JSP_TAG_ATTRIBUTE_VALUE_SQUOTE}); + // abort early when an unescaped single quote is found in the EL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_SQUOTE) || fEmbeddedContainer.getLastRegion().getType().equals(JSP_TAG_ATTRIBUTE_VALUE_SQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; +} +<ST_XML_ATTRIBUTE_VALUE_SQUOTED> \x23\x7b { + int enterState = yystate(); + yybegin(ST_JSP_SQUOTED_VBL); + assembleEmbeddedContainer(JSP_VBL_OPEN, new String[]{JSP_VBL_CLOSE, XML_TAG_ATTRIBUTE_VALUE_SQUOTE, JSP_TAG_ATTRIBUTE_VALUE_SQUOTE}); + // abort early when an unescaped single quote is found in the VBL + if(fEmbeddedContainer.getLastRegion().getType().equals(XML_TAG_ATTRIBUTE_VALUE_SQUOTE) || fEmbeddedContainer.getLastRegion().getType().equals(JSP_TAG_ATTRIBUTE_VALUE_SQUOTE)) { + yybegin(ST_ABORT_EMBEDDED); + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + } + else { + yybegin(enterState); + } + return PROXY_CONTEXT; +} + +// unescaped double quote, return as ending region +<ST_JSP_DQUOTED_EL,ST_JSP_DQUOTED_VBL> ["] { + return isJspTag() ? JSP_TAG_ATTRIBUTE_VALUE_DQUOTE: XML_TAG_ATTRIBUTE_VALUE_DQUOTE; +} +// unescaped single quote, return as ending region +<ST_JSP_SQUOTED_EL,ST_JSP_SQUOTED_VBL> ['] { + return isJspTag() ? JSP_TAG_ATTRIBUTE_VALUE_SQUOTE : XML_TAG_ATTRIBUTE_VALUE_SQUOTE; +} + + +// paired escaped double quotes +<ST_JSP_DQUOTED_EL> \\\" ([^\\]|\\[^"}]+)* \\\" { + return JSP_EL_QUOTED_CONTENT; +} +// everything else EL +<ST_JSP_DQUOTED_EL> ([^\\}"]|\\[^"])+ { + return JSP_EL_CONTENT; +} +<ST_JSP_DQUOTED_EL> \\\" { + return JSP_EL_CONTENT; +} +// paired escaped quotes +<ST_JSP_SQUOTED_EL> \\\' ([^\\]|\\[^'}]+)* \\\' { + return JSP_EL_QUOTED_CONTENT; +} +// everything else EL +<ST_JSP_SQUOTED_EL> ([^\\}']|\\[^'])+ { + return JSP_EL_CONTENT; +} +<ST_JSP_SQUOTED_EL> \\\' { + return JSP_EL_CONTENT; +} +<ST_JSP_DQUOTED_EL,ST_JSP_SQUOTED_EL> } { + return JSP_EL_CLOSE; +} + + +// paired escaped quotes +<ST_JSP_DQUOTED_VBL> \\\" ([^\\]|\\[^"}]+)* \\\" { + return JSP_VBL_QUOTED_CONTENT; +} +// everything else VBL +<ST_JSP_DQUOTED_VBL> ([^\\}"]|\\[^"])+ { + return JSP_VBL_CONTENT; +} +<ST_JSP_DQUOTED_VBL> \\\" { + return JSP_VBL_CONTENT; +} +// paired escaped quotes +<ST_JSP_SQUOTED_VBL> \\\' ([^\\]|\\[^'}]+)* \\\' { + return JSP_VBL_QUOTED_CONTENT; +} +// everything else VBL +<ST_JSP_SQUOTED_VBL> ([^\\}']|\\[^'])+ { + return JSP_VBL_CONTENT; +} +<ST_JSP_SQUOTED_VBL> \\\' { + return JSP_VBL_CONTENT; +} + +<ST_JSP_DQUOTED_VBL,ST_JSP_SQUOTED_VBL> } { + return JSP_VBL_CLOSE; +} + + +// XML content area EL +<YYINITIAL> \x24\x7b[^\x7d]*/\x7d { + yybegin(ST_JSP_EL); + if(yylength() > 2) + yypushback(yylength() - 2); + fELlevel++; + fEmbeddedHint = XML_CONTENT; + fEmbeddedPostState = YYINITIAL; + assembleEmbeddedContainer(JSP_EL_OPEN, JSP_EL_CLOSE); + fEmbeddedHint = XML_CONTENT; + yybegin(YYINITIAL); + return PROXY_CONTEXT; +} +// XML attribute name EL +<ST_XML_ATTRIBUTE_NAME> \x24\x7b[^\x7d]*/\x7d { + yybegin(ST_JSP_EL); + if(yylength() > 2) + yypushback(yylength() - 2); + fELlevel++; + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + assembleEmbeddedContainer(JSP_EL_OPEN, JSP_EL_CLOSE); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + yybegin(ST_XML_ATTRIBUTE_NAME); + return PROXY_CONTEXT; +} +<ST_JSP_EL> \x24\x7b { + fELlevel++; + if(fELlevel == 1) { + return JSP_EL_OPEN; + } +} +// XML content area VBL +<YYINITIAL> \x23\x7b[^\x7d]*/\x7d { + yybegin(ST_JSP_VBL); + if(yylength() > 2) + yypushback(yylength() - 2); + fELlevel++; + fEmbeddedHint = XML_CONTENT; + fEmbeddedPostState = YYINITIAL; + assembleEmbeddedContainer(JSP_VBL_OPEN, JSP_VBL_CLOSE); + fEmbeddedHint = XML_CONTENT; + yybegin(YYINITIAL); + return PROXY_CONTEXT; +} +<ST_JSP_VBL> \x23\x7b { + fELlevel++; + if(fELlevel == 1) { + return JSP_VBL_OPEN; + } +} +// XML attribute name VBL +<ST_XML_ATTRIBUTE_NAME> \x23\x7b[^\x7d]*/\x7d { + yybegin(ST_JSP_VBL); + if(yylength() > 2) + yypushback(yylength() - 2); + fELlevel++; + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + assembleEmbeddedContainer(JSP_VBL_OPEN, JSP_VBL_CLOSE); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + yybegin(ST_XML_ATTRIBUTE_NAME); + return PROXY_CONTEXT; +} +// return anything not starting quotes or ending the VBL as content +<ST_JSP_VBL> [^\x7d\x22\x27]* { + return JSP_VBL_CONTENT; +} +// return anything not starting quotes or ending the EL as content +<ST_JSP_EL> [^\x7d\x22\x27]* { + return JSP_EL_CONTENT; +} + + +// quotes +<ST_JSP_EL> \x22 { + yybegin(ST_JSP_EL_DQUOTES); + return JSP_EL_DQUOTE; +} +<ST_JSP_EL_DQUOTES> \x22 { + yybegin(ST_JSP_EL); + return JSP_EL_DQUOTE; +} +<ST_JSP_EL_DQUOTES> [^\x22]+/\x22 { + yybegin(ST_JSP_EL_DQUOTES_END); + return JSP_EL_QUOTED_CONTENT; +} +<ST_JSP_EL_DQUOTES_END> \x22 { + yybegin(ST_JSP_EL); + return JSP_EL_DQUOTE; +} +<ST_JSP_EL> \x27 { + yybegin(ST_JSP_EL_SQUOTES); + return JSP_EL_SQUOTE; +} +<ST_JSP_EL_SQUOTES> \x27 { + yybegin(ST_JSP_EL); + return JSP_EL_SQUOTE; +} +<ST_JSP_EL_SQUOTES> [^\x27]+/\x27 { + yybegin(ST_JSP_EL_SQUOTES_END); + return JSP_EL_QUOTED_CONTENT; +} +<ST_JSP_EL_SQUOTES_END> \x27 { + yybegin(ST_JSP_EL); + return JSP_EL_SQUOTE; +} +// quotes +<ST_JSP_VBL> \x22 { + yybegin(ST_JSP_VBL_DQUOTES); + return JSP_VBL_DQUOTE; +} +<ST_JSP_VBL_DQUOTES> \x22 { + yybegin(ST_JSP_VBL); + return JSP_VBL_DQUOTE; +} +<ST_JSP_VBL_DQUOTES> [^\x22]+/\x22 { + yybegin(ST_JSP_VBL_DQUOTES_END); + return JSP_VBL_QUOTED_CONTENT; +} +<ST_JSP_VBL_DQUOTES_END> \x22 { + yybegin(ST_JSP_VBL); + return JSP_VBL_DQUOTE; +} +<ST_JSP_VBL> \x27 { + yybegin(ST_JSP_VBL_SQUOTES); + return JSP_VBL_SQUOTE; +} +<ST_JSP_VBL_SQUOTES> \x27 { + yybegin(ST_JSP_VBL); + return JSP_VBL_SQUOTE; +} +<ST_JSP_VBL_SQUOTES> [^\x27]+/\x27 { + yybegin(ST_JSP_VBL_SQUOTES_END); + return JSP_VBL_QUOTED_CONTENT; +} +<ST_JSP_VBL_SQUOTES_END> \x27 { + yybegin(ST_JSP_VBL); + return JSP_VBL_SQUOTE; +} + +// unquoted content +<ST_JSP_EL> ([^}\x27\x22\x24]|\x24[^\x7b\x27\x22])+ { + //System.out.println(JSP_EL_CONTENT+ ":[" + yytext() + "]"); + return JSP_EL_CONTENT; +} +<ST_JSP_EL> } { + fELlevel--; + if(fELlevel == 0) { + yybegin(YYINITIAL); + return JSP_EL_CLOSE; + } + return JSP_EL_CONTENT; +} +<ST_JSP_VBL> } { + fELlevel--; + if(fELlevel == 0) { + yybegin(YYINITIAL); + return JSP_VBL_CLOSE; + } + return JSP_VBL_CONTENT; +} +// EL unquoted in tag (section 2.1 declares it as valid in template text (XML_CONTENT) or attribute values +<ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> \x24\x7b[^\x7d]+/\x7d { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", el-unquoted");//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + if(yylength() > 2) + yypushback(yylength() -2); + if(Debug.debugTokenizer) + dump("EL in attr value");//$NON-NLS-1$ + yybegin(ST_JSP_EL); + fELlevel++; + assembleEmbeddedContainer(JSP_EL_OPEN, new String[]{JSP_EL_CLOSE}); + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + return PROXY_CONTEXT; +} +// VBL unquoted in tag or attribute values +<ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> \x23\x7b[^\x7d]+/\x7d { + if (Debug.debugTokenizer) { + System.out.println("begin embedded region: " + fEmbeddedHint+", el-unquoted");//$NON-NLS-1$ + } + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + fStateStack.push(yystate()); + if(yylength() > 2) + yypushback(yylength() -2); + if(Debug.debugTokenizer) + dump("VBL in attr value");//$NON-NLS-1$ + yybegin(ST_JSP_VBL); + fELlevel++; + assembleEmbeddedContainer(JSP_VBL_OPEN, new String[]{JSP_VBL_CLOSE}); + fStateStack.pop(); + yybegin(ST_XML_ATTRIBUTE_NAME); + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + return PROXY_CONTEXT; +} + +<ST_JSP_COMMENT> .|\r|\n { + if(Debug.debugTokenizer) + dump("\nJSP comment text");//$NON-NLS-1$ + return scanJSPCommentText(); +} +<ST_JSP_COMMENT_END> {jspCommentEnd} { + if(Debug.debugTokenizer) + dump("\nJSP comment close");//$NON-NLS-1$ + yybegin(YYINITIAL); + return JSP_COMMENT_CLOSE; +} + +// XML misc + +{CDStart} { + if(Debug.debugTokenizer) + dump("\nCDATA start");//$NON-NLS-1$ + fStateStack.push(yystate()); + yybegin(ST_CDATA_TEXT); + return XML_CDATA_OPEN; +} +<ST_CDATA_TEXT> .|\r|\n { + if(Debug.debugTokenizer) + dump("CDATA text");//$NON-NLS-1$ + fEmbeddedPostState = ST_CDATA_TEXT; + fEmbeddedHint = XML_CDATA_TEXT; + String returnedContext = doScan("]]>", false, true, true, XML_CDATA_TEXT, ST_CDATA_END, ST_CDATA_END);//$NON-NLS-1$ + if(returnedContext == XML_CDATA_TEXT) + yybegin(ST_CDATA_END); + return returnedContext; +} +<ST_CDATA_END> {CDEnd} { + if(Debug.debugTokenizer) + dump("CDATA end");//$NON-NLS-1$ + yybegin(fStateStack.pop()); + return XML_CDATA_CLOSE; +} + +<YYINITIAL> {PEReference} { + if(Debug.debugTokenizer) + dump("\nPEReference");//$NON-NLS-1$ + return XML_PE_REFERENCE; +} +<YYINITIAL> {CharRef} { + if(Debug.debugTokenizer) + dump("\nCharRef");//$NON-NLS-1$ + return XML_CHAR_REFERENCE; +} +<YYINITIAL> {EntityRef} { + if(Debug.debugTokenizer) + dump("\nEntityRef");//$NON-NLS-1$ + return XML_ENTITY_REFERENCE; +} + +<YYINITIAL> {PIstart} { + if(Debug.debugTokenizer) + dump("\nprocessing instruction start");//$NON-NLS-1$ + yybegin(ST_PI); + return XML_PI_OPEN; +} +// the next four are order dependent +<ST_PI> ((X|x)(M|m)(L|l)) { + if(Debug.debugTokenizer) + dump("XML processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +<ST_PI> ([iI][mM][pP][oO][rR][tT]) { + if(Debug.debugTokenizer) + dump("DHTML processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +<ST_PI> xml-stylesheet { + if(Debug.debugTokenizer) + dump("XSL processing instruction target");//$NON-NLS-1$ + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +<ST_PI> {Name} { + if(Debug.debugTokenizer) + dump("processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_CONTENT; + yybegin(ST_PI_WS); + return XML_TAG_NAME; +} +<ST_PI_WS> {S}+ { + yybegin(ST_PI_CONTENT); + return WHITE_SPACE; +} +<ST_PI, ST_PI_WS> \?> { + if(Debug.debugTokenizer) + dump("processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} +<ST_PI_CONTENT> . { + // block scan until close is found + return doScan("?>", false, false, false, XML_PI_CONTENT, ST_XML_PI_TAG_CLOSE, ST_XML_PI_TAG_CLOSE); +} +<ST_PI_CONTENT,ST_XML_PI_TAG_CLOSE> \?> { + // ended with nothing inside + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} + +<ST_XML_PI_ATTRIBUTE_NAME, ST_XML_PI_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_XML_PI_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +<ST_XML_PI_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("XML processing instruction '='");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_PI_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +/* the value was found, look for the next name */ +<ST_XML_PI_ATTRIBUTE_VALUE> {AttValue} { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} +/* the PI's close was found */ +<ST_XML_PI_EQUALS, ST_XML_PI_ATTRIBUTE_NAME, ST_XML_PI_ATTRIBUTE_VALUE> {PIend} { + if(Debug.debugTokenizer) + dump("XML processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} +// DHTML +<ST_DHTML_ATTRIBUTE_NAME, ST_DHTML_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_DHTML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +<ST_DHTML_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("DHTML processing instruction '='");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_DHTML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +/* the value was found, look for the next name */ +<ST_DHTML_ATTRIBUTE_VALUE> {AttValue} | ([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} +/* The DHTML PI's close was found */ +<ST_DHTML_EQUALS, ST_DHTML_ATTRIBUTE_NAME, ST_DHTML_ATTRIBUTE_VALUE> [/]*> { + if(Debug.debugTokenizer) + dump("DHTML processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} + +// XML declarations + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_JSP_ATTRIBUTE_VALUE> {genericTagOpen}! { + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("\ndeclaration start");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION); + return XML_DECLARATION_OPEN; +} +<ST_XML_DECLARATION> [Ee][Ll][Ee][Mm][Ee][Nn][Tt] { + if(Debug.debugTokenizer) + dump("element");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION); + return XML_ELEMENT_DECLARATION; +} +<ST_XML_DECLARATION> [Dd][Oo][Cc][Tt][Yy][Pp][Ee] { + if(Debug.debugTokenizer) + dump("doctype");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_DECLARATION); + return XML_DOCTYPE_DECLARATION; +} +<ST_XML_DECLARATION> [Aa][Tt][Tt][Ll][Ii][Ss][Tt] { + if(Debug.debugTokenizer) + dump("attlist");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION); + return XML_ATTLIST_DECLARATION; +} + +// begin DOCTYPE handling procedure +<ST_XML_DOCTYPE_DECLARATION, ST_XML_DOCTYPE_EXTERNAL_ID, ST_XML_DOCTYPE_ID_SYSTEM, ST_XML_DOCTYPE_ID_PUBLIC, ST_XML_DECLARATION_CLOSE> \[[^\]]*\] { + return XML_DOCTYPE_INTERNAL_SUBSET; +} + +<ST_XML_DOCTYPE_DECLARATION> {Name} { + if(Debug.debugTokenizer) + dump("doctype type");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_EXTERNAL_ID); + return XML_DOCTYPE_NAME; +} +<ST_XML_DOCTYPE_EXTERNAL_ID> [Pp][Uu][Bb][Ll][Ii][Cc] { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + fEmbeddedHint = XML_DOCTYPE_EXTERNAL_ID_PUBREF; + yybegin(ST_XML_DOCTYPE_ID_PUBLIC); + return XML_DOCTYPE_EXTERNAL_ID_PUBLIC; +} +<ST_XML_DOCTYPE_EXTERNAL_ID> [Ss][Yy][Ss][Tt][Ee][Mm] { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + fEmbeddedHint = XML_DOCTYPE_EXTERNAL_ID_SYSREF; + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_SYSTEM; +} +<ST_XML_DOCTYPE_ID_PUBLIC> {AttValue}|{PubidLiteral} { + if(Debug.debugTokenizer) + dump("doctype public reference");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_PUBREF; +} +<ST_XML_DOCTYPE_ID_SYSTEM> {AttValue}|{SystemLiteral} { + if(Debug.debugTokenizer) + dump("doctype system reference");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_DECLARATION_CLOSE); + return XML_DOCTYPE_EXTERNAL_ID_SYSREF; +} +// end DOCTYPE handling + +// begin ELEMENT handling procedure +<ST_XML_ELEMENT_DECLARATION> {AttValue}|{PubidLiteral} { + if(Debug.debugTokenizer) + dump("elementdecl name");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_ELEMENT_DECLARATION_CONTENT); + return XML_ELEMENT_DECL_NAME; +} +<ST_XML_ELEMENT_DECLARATION_CONTENT> [^>]* { + if(Debug.debugTokenizer) + dump("elementdecl contentspec");//$NON-NLS-1$ + return XML_ELEMENT_DECL_CONTENT; +} + +<ST_XML_ELEMENT_DECLARATION_CONTENT> {genericTagClose} { + if(Debug.debugTokenizer) + dump("elementdecl close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; +} +// end ELEMENT handling + +// begin ATTLIST handling procedure +<ST_XML_ATTLIST_DECLARATION> {AttValue}|{PubidLiteral} { + if(Debug.debugTokenizer) + dump("attlist name");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_ATTLIST_DECLARATION_CONTENT); + return XML_ATTLIST_DECL_NAME; +} +<ST_XML_ATTLIST_DECLARATION_CONTENT> [^>]* { + if(Debug.debugTokenizer) + dump("attlist contentspec");//$NON-NLS-1$ + return XML_ATTLIST_DECL_CONTENT; +} + +<ST_XML_ATTLIST_DECLARATION_CONTENT> {genericTagClose} { + if(Debug.debugTokenizer) + dump("attlist close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; +} +// end ATTLIST handling + +<ST_XML_DECLARATION, ST_XML_DOCTYPE_DECLARATION, ST_XML_DOCTYPE_EXTERNAL_ID, ST_XML_ATTLIST_DECLARATION, ST_XML_ELEMENT_DECLARATION, ST_XML_DECLARATION_CLOSE, ST_XML_DOCTYPE_ID_PUBLIC, ST_XML_DOCTYPE_ID_SYSTEM, ST_XML_DOCTYPE_EXTERNAL_ID> {genericTagClose} { + if(Debug.debugTokenizer) + dump("declaration end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; +} +// end DECLARATION handling + +<YYINITIAL> ([^<&%\x24\x23]*|\x23+|\x24+|[&%]{S}+{Name}[^&%<]*|[\x24\x23][^\x7b<&%][^<&%\x24\x23]*|\\[\x24\x23][\x7b]|[&%]{Name}([^;&%<]*|{S}+;*)) { + if(Debug.debugTokenizer) + dump("\nXML content");//$NON-NLS-1$ + return XML_CONTENT; +} + +<YYINITIAL> (%)+ { + if(Debug.debugTokenizer) + dump("non-reference %");//$NON-NLS-1$ + return XML_CONTENT; +} + +<ST_BLOCK_TAG_SCAN> .|\r|\n { + return doBlockTagScan(); + } + +. { + if (Debug.debugTokenizer) + System.out.println("!!!unexpected!!!: \"" + yytext() + "\":" + //$NON-NLS-2$//$NON-NLS-1$ + yychar + "-" + (yychar + yylength()));//$NON-NLS-1$ + return UNDEFINED; +} + +\040 { + if(Debug.debugTokenizer) + dump("SPACE");//$NON-NLS-1$ + return WHITE_SPACE; +} +\011 { + if(Debug.debugTokenizer) + dump("0x9");//$NON-NLS-1$ + return WHITE_SPACE; +} +\015 +{ + if(Debug.debugTokenizer) + dump("CARRIAGE RETURN");//$NON-NLS-1$ + return WHITE_SPACE; +} +\012 { + if(Debug.debugTokenizer) + dump("LINE FEED");//$NON-NLS-1$ + return WHITE_SPACE; +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.java b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.java new file mode 100644 index 0000000..b4607a2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.java
@@ -0,0 +1,1937 @@ +/* The following code was generated by JFlex 1.2.2 on 10/17/07 4:27 AM */ + +/******************************************************************************* + * Copyright (c) 2004, 2007 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.wst.xml.core.internal.parser; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTokenizer; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.eclipse.wst.xml.core.internal.Logger; +import org.eclipse.wst.xml.core.internal.parser.regions.XMLParserRegionFactory; +import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; + + +/** + * This class is a scanner generated by + * <a href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex</a> 1.2.2 + * on 10/17/07 4:27 AM from the specification file + * <tt>file:/D:/eclipse.wtp/workspace/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.jflex</tt> + */ +public class XMLTokenizer implements BlockTokenizer, DOMRegionContext { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int ST_XML_DOCTYPE_EXTERNAL_ID = 23; + final public static int ST_XML_ELEMENT_DECLARATION_CONTENT = 27; + final public static int ST_DHTML_ATTRIBUTE_NAME = 12; + final public static int ST_XML_PI_TAG_CLOSE = 11; + final public static int ST_XML_DECLARATION_CLOSE = 21; + final public static int ST_XML_PI_ATTRIBUTE_VALUE = 10; + final public static int ST_DHTML_EQUALS = 13; + final public static int ST_XML_TAG_NAME = 16; + final public static int ST_XML_ATTRIBUTE_VALUE = 19; + final public static int ST_DHTML_ATTRIBUTE_VALUE = 14; + final public static int ST_XML_DOCTYPE_ID_SYSTEM = 25; + final public static int ST_XML_ATTRIBUTE_NAME = 17; + final public static int ST_XML_ELEMENT_DECLARATION = 26; + final public static int ST_XML_DOCTYPE_DECLARATION = 22; + final public static int ST_XML_ATTLIST_DECLARATION = 28; + final public static int ST_XML_COMMENT_END = 4; + final public static int ST_CDATA_TEXT = 1; + final public static int ST_DHTML_TAG_CLOSE = 15; + final public static int ST_XML_COMMENT = 3; + final public static int ST_PI_CONTENT = 7; + final public static int ST_PI_WS = 6; + final public static int ST_CDATA_END = 2; + final public static int ST_XML_ATTLIST_DECLARATION_CONTENT = 29; + final public static int ST_BLOCK_TAG_SCAN = 30; + final public static int ST_XML_PI_EQUALS = 9; + final public static int ST_XML_DECLARATION = 20; + final public static int YYINITIAL = 0; + final public static int ST_XML_DOCTYPE_ID_PUBLIC = 24; + final public static int ST_XML_EQUALS = 18; + final public static int ST_PI = 5; + final public static int ST_XML_PI_ATTRIBUTE_NAME = 8; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = + "\11\0\1\5\1\22\2\0\1\14\22\0\1\14\1\21\1\11\1\55"+ + "\1\16\1\17\1\12\1\13\1\16\1\16\1\16\1\16\1\16\1\7"+ + "\1\6\1\3\12\15\1\10\1\61\1\1\1\45\1\2\1\4\1\16"+ + "\1\34\1\62\1\32\1\33\1\50\1\57\1\36\1\36\1\42\1\36"+ + "\1\36\1\27\1\25\1\44\1\43\1\47\1\36\1\40\1\56\1\35"+ + "\1\60\2\36\1\23\1\46\1\36\1\31\1\0\1\20\1\0\1\10"+ + "\1\0\1\52\1\62\1\63\1\53\1\37\1\57\1\36\1\66\1\42"+ + "\2\36\1\30\1\26\1\44\1\43\1\47\1\36\1\40\1\41\1\51"+ + "\1\60\1\36\1\36\1\24\1\54\1\36\1\0\1\0\72\0\1\65"+ + "\10\0\27\64\1\0\37\64\1\0\72\64\2\0\13\64\2\0\10\64"+ + "\1\0\65\64\1\0\104\64\11\0\44\64\3\0\2\64\4\0\36\64"+ + "\70\0\131\64\22\0\7\64\16\0\2\65\56\0\106\65\32\0\2\65"+ + "\44\0\1\64\1\65\3\64\1\0\1\64\1\0\24\64\1\0\54\64"+ + "\1\0\7\64\3\0\1\64\1\0\1\64\1\0\1\64\1\0\1\64"+ + "\1\0\22\64\15\0\14\64\1\0\102\64\1\0\14\64\1\0\44\64"+ + "\1\0\4\65\11\0\65\64\2\0\2\64\2\0\2\64\3\0\34\64"+ + "\2\0\10\64\2\0\2\64\67\0\46\64\2\0\1\64\7\0\46\64"+ + "\12\0\21\65\1\0\27\65\1\0\3\65\1\0\1\65\1\0\2\65"+ + "\1\0\1\65\13\0\33\64\5\0\3\64\56\0\32\64\5\0\1\65"+ + "\12\64\10\65\15\0\12\65\6\0\1\65\107\64\2\0\5\64\1\0"+ + "\17\64\1\0\4\64\1\0\1\64\17\65\2\64\2\65\1\0\4\65"+ + "\2\0\12\65\u0207\0\3\65\1\0\65\64\2\0\1\65\1\64\20\65"+ + "\3\0\4\65\3\0\12\64\2\65\2\0\12\65\21\0\3\65\1\0"+ + "\10\64\2\0\2\64\2\0\26\64\1\0\7\64\1\0\1\64\3\0"+ + "\4\64\2\0\1\65\1\0\7\65\2\0\2\65\2\0\3\65\11\0"+ + "\1\65\4\0\2\64\1\0\3\64\2\65\2\0\12\65\2\64\20\0"+ + "\1\65\2\0\6\64\4\0\2\64\2\0\26\64\1\0\7\64\1\0"+ + "\2\64\1\0\2\64\1\0\2\64\2\0\1\65\1\0\5\65\4\0"+ + "\2\65\2\0\3\65\13\0\4\64\1\0\1\64\7\0\12\65\2\65"+ + "\3\64\14\0\3\65\1\0\7\64\1\0\1\64\1\0\3\64\1\0"+ + "\26\64\1\0\7\64\1\0\2\64\1\0\5\64\2\0\1\65\1\64"+ + "\10\65\1\0\3\65\1\0\3\65\22\0\1\64\5\0\12\65\21\0"+ + "\3\65\1\0\10\64\2\0\2\64\2\0\26\64\1\0\7\64\1\0"+ + "\2\64\2\0\4\64\2\0\1\65\1\64\6\65\3\0\2\65\2\0"+ + "\3\65\10\0\2\65\4\0\2\64\1\0\3\64\4\0\12\65\22\0"+ + "\2\65\1\0\6\64\3\0\3\64\1\0\4\64\3\0\2\64\1\0"+ + "\1\64\1\0\2\64\3\0\2\64\3\0\3\64\3\0\10\64\1\0"+ + "\3\64\4\0\5\65\3\0\3\65\1\0\4\65\11\0\1\65\17\0"+ + "\11\65\21\0\3\65\1\0\10\64\1\0\3\64\1\0\27\64\1\0"+ + "\12\64\1\0\5\64\4\0\7\65\1\0\3\65\1\0\4\65\7\0"+ + "\2\65\11\0\2\64\4\0\12\65\22\0\2\65\1\0\10\64\1\0"+ + "\3\64\1\0\27\64\1\0\12\64\1\0\5\64\4\0\7\65\1\0"+ + "\3\65\1\0\4\65\7\0\2\65\7\0\1\64\1\0\2\64\4\0"+ + "\12\65\22\0\2\65\1\0\10\64\1\0\3\64\1\0\27\64\1\0"+ + "\20\64\4\0\6\65\2\0\3\65\1\0\4\65\11\0\1\65\10\0"+ + "\2\64\4\0\12\65\221\0\56\64\1\0\1\64\1\65\2\64\7\65"+ + "\5\0\6\64\1\65\10\65\1\0\12\65\47\0\2\64\1\0\1\64"+ + "\2\0\2\64\1\0\1\64\2\0\1\64\6\0\4\64\1\0\7\64"+ + "\1\0\3\64\1\0\1\64\1\0\1\64\2\0\2\64\1\0\2\64"+ + "\1\0\1\64\1\65\2\64\6\65\1\0\2\65\1\64\2\0\5\64"+ + "\1\0\1\65\1\0\6\65\2\0\12\65\76\0\2\65\6\0\12\65"+ + "\13\0\1\65\1\0\1\65\1\0\1\65\4\0\2\65\10\64\1\0"+ + "\41\64\7\0\24\65\1\0\6\65\4\0\6\65\1\0\1\65\1\0"+ + "\25\65\3\0\7\65\1\0\1\65\346\0\46\64\12\0\47\64\11\0"+ + "\1\64\1\0\2\64\1\0\3\64\1\0\1\64\1\0\2\64\1\0"+ + "\5\64\51\0\1\64\1\0\1\64\1\0\1\64\13\0\1\64\1\0"+ + "\1\64\1\0\1\64\3\0\2\64\3\0\1\64\5\0\3\64\1\0"+ + "\1\64\1\0\1\64\1\0\1\64\1\0\1\64\3\0\2\64\3\0"+ + "\2\64\1\0\1\64\50\0\1\64\11\0\1\64\2\0\1\64\2\0"+ + "\2\64\7\0\2\64\1\0\1\64\1\0\7\64\50\0\1\64\4\0"+ + "\1\64\10\0\1\64\u0c06\0\234\64\4\0\132\64\6\0\26\64\2\0"+ + "\6\64\2\0\46\64\2\0\6\64\2\0\10\64\1\0\1\64\1\0"+ + "\1\64\1\0\1\64\1\0\37\64\2\0\65\64\1\0\7\64\1\0"+ + "\1\64\3\0\3\64\1\0\7\64\3\0\4\64\2\0\6\64\4\0"+ + "\15\64\5\0\3\64\1\0\7\64\323\0\15\65\4\0\1\65\104\0"+ + "\1\64\3\0\2\64\2\0\1\64\121\0\3\64\u0e82\0\1\65\1\0"+ + "\1\64\31\0\11\64\6\65\1\0\5\65\13\0\124\64\4\0\2\65"+ + "\2\0\2\65\2\0\132\64\1\0\3\65\6\0\50\64\u1cd3\0\u51a6\64"+ + "\u0c5a\0\u2ba4\64\134\0\u0800\0\u1ffe\0\2\0"; + + /** + * Translates characters to character classes + */ + final private static char [] yycmap = yy_unpack_cmap(yycmap_packed); + + /** + * Translates a state to a row index in the transition table + */ + final private static int yy_rowMap [] = { + 0, 55, 110, 165, 220, 275, 330, 385, 440, 495, + 550, 605, 660, 715, 770, 825, 880, 935, 990, 1045, + 1100, 1155, 1210, 1265, 1320, 1375, 1430, 1485, 1540, 1595, + 1650, 1705, 1760, 1815, 1870, 1925, 1980, 1925, 1980, 2035, + 1925, 1925, 1980, 2090, 2145, 2200, 2255, 2310, 2365, 2420, + 2475, 1925, 1980, 2530, 2585, 2640, 1925, 2695, 2695, 2750, + 2805, 2860, 2530, 1925, 2915, 2970, 1925, 3025, 3080, 3135, + 3190, 3245, 3300, 1925, 3355, 3410, 3465, 3520, 1925, 3575, + 3630, 3685, 3740, 3795, 1925, 3850, 3905, 3960, 4015, 4070, + 4125, 4180, 4235, 4235, 4290, 4345, 4400, 4455, 4455, 4510, + 4565, 4620, 4675, 4675, 4730, 4785, 4840, 4895, 1925, 4950, + 4950, 5005, 5060, 5115, 5170, 1925, 1925, 1980, 1925, 1925, + 5225, 5280, 5335, 5390, 5445, 5500, 5555, 5610, 1925, 5665, + 5720, 5775, 1925, 1925, 2695, 5830, 2805, 1925, 5885, 2860, + 2915, 3025, 3080, 5940, 3135, 1925, 5995, 3190, 1925, 3575, + 6050, 3685, 1925, 6105, 3740, 5225, 6160, 6215, 6270, 4015, + 1925, 6325, 6380, 4235, 6435, 4290, 1925, 6490, 6545, 6600, + 6600, 6655, 6710, 4400, 4235, 4455, 6765, 4510, 1925, 6820, + 4565, 4620, 4455, 4675, 6875, 4730, 1925, 6930, 6985, 7040, + 7040, 7095, 7150, 7205, 4950, 7260, 5005, 1925, 7315, 7370, + 7425, 7425, 7480, 7535, 7590, 7645, 7700, 7755, 7810, 1925, + 7865, 7920, 1925, 1925, 1925, 2255, 7975, 8030, 8085, 8140, + 8195, 8250, 8305, 6490, 8360, 8360, 6930, 8415, 8415, 8470, + 7315, 8525, 8525, 8580, 1925, 8635, 8690, 1925, 8745, 8800, + 8855, 8910, 8965, 9020, 9075, 9130, 6655, 7095, 9185, 7480, + 9240, 9295, 9350, 9405, 9460, 9515, 9570, 9625, 9680, 9735, + 9790, 9845, 9900, 9955, 10010, 10065, 10120, 1925, 1925, 10175, + 10230, 10285, 10340, 10395, 1925, 1925, 1925, 10450, 10505, 10560, + 10615, 10670, 10725, 1925, 10780, 4840, 5115, 10835, 10890, 10945, + 11000, 2255 + }; + + /** + * The packed transition table of the DFA + */ + final private static String yy_packed = + "\1\40\1\41\10\40\1\42\4\40\1\43\47\40\1\44"+ + "\1\45\65\44\1\46\1\47\16\46\1\50\1\46\1\51"+ + "\44\46\1\52\1\53\65\52\1\46\1\47\5\46\1\54"+ + "\12\46\1\51\45\46\1\47\2\46\1\55\1\56\2\46"+ + "\1\57\3\46\1\56\5\46\1\56\1\60\1\61\4\57"+ + "\1\46\10\57\1\62\2\57\1\46\7\57\1\46\3\57"+ + "\1\46\3\57\1\46\1\57\1\46\1\47\2\46\1\55"+ + "\1\63\6\46\1\63\5\46\1\63\44\46\1\64\1\65"+ + "\2\64\1\66\15\64\1\51\44\64\1\46\1\47\2\46"+ + "\1\67\1\56\2\46\1\70\3\46\1\56\5\46\1\56"+ + "\6\70\1\46\13\70\1\46\7\70\1\46\3\70\1\46"+ + "\3\70\1\46\1\70\1\46\1\47\2\46\1\67\1\56"+ + "\2\46\1\70\3\46\1\56\5\46\1\56\6\70\1\46"+ + "\13\70\1\71\7\70\1\46\3\70\1\46\3\70\1\46"+ + "\1\70\1\72\1\47\1\46\1\73\1\74\1\56\3\72"+ + "\1\75\1\72\1\76\1\56\5\72\1\56\44\72\1\46"+ + "\1\47\2\46\1\77\15\46\1\51\45\46\1\47\1\100"+ + "\1\101\1\46\1\56\2\46\1\102\3\46\1\56\5\46"+ + "\1\56\6\102\1\46\13\102\1\46\7\102\1\46\3\102"+ + "\1\46\3\102\1\46\1\102\1\46\1\47\1\100\1\101"+ + "\1\46\1\56\2\46\1\102\3\46\1\56\5\46\1\56"+ + "\6\102\1\46\13\102\1\103\7\102\1\46\3\102\1\46"+ + "\3\102\1\46\1\102\1\104\1\47\1\100\1\105\1\104"+ + "\1\56\3\104\1\106\1\104\1\107\1\56\5\104\1\56"+ + "\44\104\1\46\1\47\3\46\1\56\6\46\1\56\5\46"+ + "\1\56\44\46\1\110\1\111\1\112\1\113\4\110\1\114"+ + "\12\110\6\115\1\110\13\115\1\110\7\115\1\110\3\115"+ + "\1\110\3\115\1\110\1\115\1\46\1\111\1\112\1\113"+ + "\1\46\1\56\2\46\1\116\3\46\1\56\5\46\1\56"+ + "\6\116\1\46\13\116\1\46\7\116\1\46\3\116\1\46"+ + "\3\116\1\46\1\116\1\46\1\111\1\112\1\113\1\46"+ + "\1\56\2\46\1\116\3\46\1\56\5\46\1\56\6\116"+ + "\1\46\13\116\1\117\7\116\1\46\3\116\1\46\3\116"+ + "\1\46\1\116\1\120\1\111\1\112\1\121\1\120\1\56"+ + "\3\120\1\122\1\120\1\123\1\56\5\120\1\56\44\120"+ + "\1\46\1\124\1\125\2\46\1\56\6\46\1\56\5\46"+ + "\1\56\10\46\1\126\1\127\2\46\1\130\10\46\1\130"+ + "\1\46\1\127\1\126\14\46\1\47\1\125\2\46\1\56"+ + "\6\46\1\56\5\46\1\56\6\46\1\131\36\46\1\47"+ + "\1\125\2\46\1\56\2\46\1\132\3\46\1\56\5\46"+ + "\1\56\6\132\1\131\13\132\1\46\7\132\1\46\3\132"+ + "\1\46\3\132\1\46\1\132\1\46\1\47\1\125\2\46"+ + "\1\56\6\46\1\56\5\46\1\56\6\46\1\131\7\46"+ + "\1\133\5\46\1\134\6\46\1\133\10\46\1\135\1\47"+ + "\1\125\1\136\1\135\1\56\3\135\1\137\1\135\1\140"+ + "\1\56\5\135\1\56\6\135\1\141\35\135\1\142\1\47"+ + "\1\125\1\143\1\142\1\56\3\142\1\144\1\142\1\145"+ + "\1\56\5\142\1\56\6\142\1\146\35\142\1\147\1\47"+ + "\1\125\1\150\1\147\1\56\3\147\1\151\1\147\1\152"+ + "\1\56\5\147\1\56\44\147\1\153\1\154\1\155\64\153"+ + "\1\156\1\47\1\125\1\157\1\156\1\56\3\156\1\160"+ + "\1\156\1\161\1\56\5\156\1\56\44\156\1\162\1\163"+ + "\1\164\64\162\1\165\1\166\65\165\1\40\1\0\10\40"+ + "\1\0\4\40\1\0\47\40\3\0\1\167\1\170\14\0"+ + "\1\171\52\0\1\172\2\0\1\173\3\0\1\172\5\0"+ + "\1\172\6\173\1\0\13\173\1\0\7\173\1\174\3\173"+ + "\1\0\3\173\1\0\1\173\5\0\1\172\2\0\1\175"+ + "\3\0\1\172\5\0\1\172\6\175\1\0\13\175\1\0"+ + "\7\175\1\0\3\175\1\0\3\175\1\0\1\175\110\0"+ + "\1\176\65\0\1\177\55\0\1\200\61\0\1\201\71\0"+ + "\1\56\6\0\1\56\5\0\1\56\52\0\3\57\4\0"+ + "\1\57\5\0\6\57\1\0\13\57\1\0\7\57\1\0"+ + "\3\57\1\0\5\57\6\0\3\57\4\0\1\57\5\0"+ + "\2\57\2\202\2\57\1\0\13\57\1\0\7\57\1\0"+ + "\3\57\1\0\5\57\6\0\3\57\4\0\1\57\5\0"+ + "\2\57\1\202\1\203\2\57\1\0\13\57\1\0\7\57"+ + "\1\0\3\57\1\0\5\57\6\0\3\57\4\0\1\57"+ + "\5\0\2\57\2\204\2\57\1\0\13\57\1\0\7\57"+ + "\1\0\3\57\1\0\5\57\5\0\1\63\6\0\1\63"+ + "\5\0\1\63\46\0\1\205\66\0\1\206\72\0\3\70"+ + "\4\0\1\70\5\0\6\70\1\0\13\70\1\0\7\70"+ + "\1\0\3\70\1\0\5\70\1\72\2\0\1\207\1\72"+ + "\1\0\3\72\1\0\1\72\2\0\5\72\1\0\45\72"+ + "\1\0\1\206\1\207\1\72\1\0\3\72\1\0\1\72"+ + "\2\0\5\72\1\0\44\72\1\75\1\0\1\210\1\211"+ + "\1\75\1\210\3\75\1\212\1\75\2\210\5\75\1\210"+ + "\44\75\1\76\1\0\1\213\1\214\1\76\1\213\3\76"+ + "\1\213\1\76\1\212\1\213\5\76\1\213\44\76\2\0"+ + "\1\100\1\215\71\0\3\102\4\0\1\102\5\0\6\102"+ + "\1\0\13\102\1\0\7\102\1\0\3\102\1\0\5\102"+ + "\1\104\2\0\1\216\1\104\1\0\3\104\1\0\1\104"+ + "\2\0\5\104\1\0\45\104\1\0\1\100\1\217\1\104"+ + "\1\0\3\104\1\0\1\104\2\0\5\104\1\0\44\104"+ + "\1\106\1\0\1\220\1\221\1\106\1\220\3\106\1\222"+ + "\1\106\2\220\5\106\1\220\44\106\1\107\1\0\1\223"+ + "\1\224\1\107\1\223\3\107\1\223\1\107\1\222\1\223"+ + "\5\107\1\223\44\107\1\110\3\0\17\110\6\0\1\110"+ + "\13\0\1\110\7\0\1\110\3\0\1\110\3\0\1\110"+ + "\4\0\1\167\15\0\1\171\47\0\1\225\64\0\1\110"+ + "\3\0\2\110\3\114\4\110\1\114\5\110\6\115\1\110"+ + "\13\115\1\110\7\115\1\110\3\115\1\110\3\115\1\114"+ + "\1\115\6\0\3\115\4\0\1\115\5\0\6\115\1\0"+ + "\13\115\1\0\7\115\1\0\3\115\1\0\5\115\6\0"+ + "\3\116\4\0\1\116\5\0\6\116\1\0\13\116\1\0"+ + "\7\116\1\0\3\116\1\0\5\116\1\120\2\0\1\226"+ + "\1\120\1\0\3\120\1\0\1\120\2\0\5\120\1\0"+ + "\45\120\1\0\1\225\1\226\1\120\1\0\3\120\1\0"+ + "\1\120\2\0\5\120\1\0\44\120\1\122\1\0\1\227"+ + "\1\230\1\122\1\227\3\122\1\231\1\122\2\227\5\122"+ + "\1\227\44\122\1\123\1\0\1\232\1\233\1\123\1\232"+ + "\3\123\1\232\1\123\1\231\1\232\5\123\1\232\44\123"+ + "\3\0\1\167\15\0\1\234\110\0\1\235\60\0\1\236"+ + "\13\0\1\236\44\0\2\237\36\0\20\240\1\241\46\240"+ + "\6\0\3\132\4\0\1\132\5\0\6\132\1\0\13\132"+ + "\1\0\7\132\1\0\3\132\1\0\5\132\46\0\1\242"+ + "\5\0\1\242\72\0\1\243\6\0\1\135\2\0\1\244"+ + "\1\135\1\0\3\135\1\0\1\135\2\0\5\135\1\0"+ + "\44\135\1\137\1\0\1\245\1\246\1\137\1\245\3\137"+ + "\1\247\1\137\2\245\5\137\1\245\44\137\1\250\1\0"+ + "\1\251\1\252\1\253\1\251\3\253\1\251\1\250\1\254"+ + "\1\255\3\253\1\250\1\253\1\255\6\253\1\250\32\253"+ + "\2\250\1\253\1\141\2\240\1\256\1\141\1\240\3\141"+ + "\1\240\1\141\2\240\3\141\1\257\1\141\1\240\44\141"+ + "\1\142\2\0\1\260\1\142\1\0\3\142\1\0\1\142"+ + "\2\0\5\142\1\0\44\142\1\144\2\261\1\262\1\144"+ + "\1\261\3\144\1\263\1\144\2\261\5\144\1\261\44\144"+ + "\1\145\2\264\1\265\1\145\1\264\3\145\1\264\1\145"+ + "\1\263\1\264\5\145\1\264\44\145\1\146\2\240\1\266"+ + "\1\146\1\240\3\146\1\240\1\146\2\240\3\146\1\267"+ + "\1\146\1\240\44\146\1\147\2\0\1\270\1\147\1\0"+ + "\3\147\1\0\1\147\2\0\5\147\1\0\44\147\1\151"+ + "\1\0\1\271\1\272\1\151\1\271\3\151\1\273\1\151"+ + "\2\271\5\151\1\271\44\151\1\274\1\0\1\275\1\276"+ + "\1\277\1\275\3\277\1\275\1\274\1\300\1\301\3\277"+ + "\1\274\1\277\1\301\6\277\1\274\32\277\2\274\1\277"+ + "\2\153\1\0\66\153\1\0\16\153\1\302\45\153\1\156"+ + "\2\0\1\303\1\156\1\0\3\156\1\0\1\156\2\0"+ + "\5\156\1\0\44\156\1\160\1\0\1\304\1\305\1\160"+ + "\1\304\3\160\1\306\1\160\2\304\5\160\1\304\44\160"+ + "\1\307\1\0\1\310\1\311\1\312\1\310\3\312\1\310"+ + "\1\307\1\313\1\314\3\312\1\307\1\312\1\314\6\312"+ + "\1\307\32\312\2\307\1\312\2\162\1\0\66\162\1\0"+ + "\16\162\1\315\45\162\7\0\1\316\21\0\1\317\42\0"+ + "\1\172\2\0\1\40\3\0\1\172\5\0\1\172\6\40"+ + "\1\0\13\40\1\0\7\40\1\0\3\40\1\0\3\40"+ + "\1\0\1\40\1\320\1\0\3\320\1\321\3\173\1\320"+ + "\1\0\1\320\1\321\1\173\1\320\1\0\2\320\1\321"+ + "\6\173\1\320\13\173\1\320\7\173\1\320\3\173\1\322"+ + "\5\173\15\0\1\323\6\0\1\324\42\0\1\320\1\0"+ + "\3\320\1\321\3\175\1\320\1\0\1\320\1\321\1\175"+ + "\1\320\1\0\2\320\1\321\6\175\1\320\13\175\1\320"+ + "\7\175\1\320\3\175\1\325\5\175\31\0\1\317\37\0"+ + "\1\326\66\0\1\327\72\0\3\57\4\0\1\57\5\0"+ + "\4\57\2\330\1\0\13\57\1\0\7\57\1\0\3\57"+ + "\1\0\5\57\6\0\3\57\4\0\1\57\5\0\4\57"+ + "\1\330\1\331\1\0\13\57\1\0\7\57\1\0\3\57"+ + "\1\0\5\57\6\0\3\57\4\0\1\57\5\0\6\57"+ + "\1\0\13\57\1\0\1\57\1\332\5\57\1\0\3\57"+ + "\1\0\5\57\1\210\1\0\7\210\1\212\55\210\1\213"+ + "\1\0\11\213\1\212\53\213\1\220\1\0\7\220\1\222"+ + "\55\220\1\223\1\0\11\223\1\222\53\223\1\227\1\0"+ + "\7\227\1\231\55\227\1\232\1\0\11\232\1\231\53\232"+ + "\32\0\1\333\30\0\1\333\40\0\1\334\13\0\1\334"+ + "\54\0\1\335\10\0\1\335\57\0\1\336\14\0\1\336"+ + "\72\0\1\337\4\0\1\245\1\0\7\245\1\247\55\245"+ + "\1\250\1\0\1\251\1\340\1\250\1\251\3\250\1\251"+ + "\1\250\1\247\1\251\5\250\1\251\44\250\1\251\1\0"+ + "\11\251\1\247\53\251\1\250\1\0\1\251\1\340\1\250"+ + "\1\251\3\250\1\251\1\250\1\341\1\251\5\250\1\251"+ + "\44\250\13\0\1\342\53\0\1\251\1\0\11\251\1\341"+ + "\53\251\11\261\1\263\55\261\13\264\1\263\53\264\1\271"+ + "\1\0\7\271\1\273\55\271\1\274\1\0\1\275\1\343"+ + "\1\274\1\275\3\274\1\275\1\274\1\273\1\275\5\274"+ + "\1\275\44\274\1\275\1\0\11\275\1\273\53\275\1\274"+ + "\1\0\1\275\1\343\1\274\1\275\3\274\1\275\1\274"+ + "\1\344\1\275\5\274\1\275\44\274\13\0\1\345\53\0"+ + "\1\275\1\0\11\275\1\344\53\275\2\153\1\0\26\153"+ + "\1\346\35\153\1\304\1\0\7\304\1\306\55\304\1\307"+ + "\1\0\1\310\1\347\1\307\1\310\3\307\1\310\1\307"+ + "\1\306\1\310\5\307\1\310\44\307\1\310\1\0\11\310"+ + "\1\306\53\310\1\307\1\0\1\310\1\347\1\307\1\310"+ + "\3\307\1\310\1\307\1\350\1\310\5\307\1\310\44\307"+ + "\13\0\1\351\53\0\1\310\1\0\11\310\1\350\53\310"+ + "\2\162\1\0\26\162\1\352\35\162\7\0\1\353\111\0"+ + "\1\354\34\0\1\320\1\0\10\320\1\0\4\320\1\0"+ + "\41\320\1\0\6\320\1\0\3\320\1\321\4\320\1\0"+ + "\1\320\1\321\2\320\1\0\2\320\1\321\36\320\1\355"+ + "\5\320\15\0\1\323\43\0\1\356\22\0\1\357\14\0"+ + "\3\357\2\0\1\357\10\0\1\357\1\0\2\357\3\0"+ + "\1\357\2\0\2\357\11\0\1\57\1\360\1\57\4\0"+ + "\1\57\5\0\6\57\1\0\13\57\1\0\7\57\1\0"+ + "\3\57\1\0\5\57\6\0\3\57\4\0\1\57\5\0"+ + "\6\57\1\0\11\57\1\361\1\57\1\0\7\57\1\0"+ + "\3\57\1\0\5\57\35\0\1\362\13\0\1\362\44\0"+ + "\2\363\63\0\2\364\75\0\1\365\13\0\1\365\44\0"+ + "\2\366\41\0\2\367\1\0\3\367\2\0\1\254\4\367"+ + "\1\0\10\367\1\0\32\367\2\0\1\367\3\0\2\370"+ + "\1\0\3\370\2\0\1\300\4\370\1\0\10\370\1\0"+ + "\32\370\2\0\1\370\2\153\1\0\27\153\1\371\34\153"+ + "\3\0\2\372\1\0\3\372\2\0\1\313\4\372\1\0"+ + "\10\372\1\0\32\372\2\0\1\372\2\162\1\0\27\162"+ + "\1\373\34\162\33\0\1\374\114\0\1\355\22\0\1\357"+ + "\14\0\3\357\2\0\1\357\10\0\1\357\1\0\2\357"+ + "\3\0\1\357\1\0\1\356\2\357\11\0\3\57\4\0"+ + "\1\57\5\0\6\57\1\0\7\57\1\375\3\57\1\0"+ + "\7\57\1\0\3\57\1\0\5\57\6\0\3\57\4\0"+ + "\1\57\5\0\6\57\1\0\6\57\1\376\4\57\1\0"+ + "\7\57\1\0\3\57\1\0\5\57\46\0\1\377\5\0"+ + "\1\377\54\0\1\u0100\63\0\1\u0101\10\0\1\u0101\55\0"+ + "\1\u0102\10\0\1\u0102\60\0\1\u0103\24\0\2\153\1\0"+ + "\30\153\1\u0104\33\153\2\162\1\0\30\162\1\u0105\33\162"+ + "\34\0\1\u0106\40\0\3\57\4\0\1\57\5\0\6\57"+ + "\1\0\13\57\1\0\3\57\1\u0107\3\57\1\0\3\57"+ + "\1\0\5\57\6\0\3\57\4\0\1\57\5\0\6\57"+ + "\1\0\3\57\1\u0108\7\57\1\0\3\57\1\u0108\3\57"+ + "\1\0\3\57\1\0\5\57\47\0\1\u0109\60\0\1\u010a"+ + "\14\0\1\u010a\54\0\1\u010b\47\0\2\u010c\72\0\1\u010d"+ + "\30\0\1\u010d\3\0\2\153\1\0\31\153\1\u010e\32\153"+ + "\2\162\1\0\31\162\1\u010f\32\162\35\0\1\u0110\37\0"+ + "\3\57\4\0\1\57\5\0\6\57\1\0\13\57\1\0"+ + "\6\57\1\u0111\1\0\3\57\1\0\5\57\5\0\1\u0112"+ + "\3\57\3\0\1\u0112\1\57\4\0\1\u0112\6\57\1\0"+ + "\13\57\1\0\7\57\1\0\3\57\1\0\5\57\37\0"+ + "\1\u0113\10\0\1\u0113\53\0\1\u0114\13\0\1\u0114\52\0"+ + "\1\u0115\13\0\1\u0115\15\0\2\153\1\0\32\153\1\u0116"+ + "\31\153\2\162\1\0\32\162\1\u0117\31\162\34\0\1\u0118"+ + "\40\0\3\57\4\0\1\57\5\0\5\57\1\u0119\1\0"+ + "\13\57\1\0\7\57\1\0\3\57\1\0\5\57\5\0"+ + "\1\u0112\6\0\1\u0112\5\0\1\u0112\44\0\2\153\1\0"+ + "\31\153\1\u011a\32\153\2\162\1\0\31\162\1\u011b\32\162"+ + "\31\0\1\u011c\43\0\3\57\4\0\1\57\5\0\6\57"+ + "\1\0\5\57\1\u011d\5\57\1\0\7\57\1\0\3\57"+ + "\1\0\5\57\2\153\1\0\26\153\1\u011e\35\153\2\162"+ + "\1\0\26\162\1\u011f\35\162\6\0\3\57\4\0\1\57"+ + "\5\0\6\57\1\0\7\57\1\u0120\3\57\1\0\7\57"+ + "\1\0\3\57\1\0\5\57\6\0\3\57\4\0\1\57"+ + "\5\0\6\57\1\0\13\57\1\0\7\57\1\0\3\57"+ + "\1\0\4\57\1\u0121\6\0\3\57\4\0\1\57\5\0"+ + "\6\57\1\0\5\57\1\u0122\5\57\1\0\7\57\1\0"+ + "\3\57\1\0\5\57\6\0\3\57\4\0\1\57\5\0"+ + "\6\57\1\0\5\57\1\u0123\5\57\1\0\7\57\1\0"+ + "\3\57\1\0\5\57\6\0\3\57\4\0\1\57\5\0"+ + "\6\57\1\0\13\57\1\0\3\57\1\u0124\3\57\1\0"+ + "\3\57\1\0\5\57"; + + /** + * The transition table of the DFA + */ + final private static int yytrans [] = yy_unpack(yy_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + // final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", //$NON-NLS-1$ + "Internal error: unknown state", //$NON-NLS-1$ + "Error: could not match input", //$NON-NLS-1$ + "Error: pushback value was too large" //$NON-NLS-1$ + }; + + /** + * YY_ATTRIBUTE[aState] contains the attributes of state <code>aState</code> + */ + private final static byte YY_ATTRIBUTE[] = { + 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 9, 1, 9, 1, 1, 9, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 9, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, + 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 9, 1, + 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, + 1, 1, 1, 9, 9, 1, 9, 9, 1, 0, 1, 0, 1, 0, 0, 0, + 9, 1, 1, 1, 9, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, + 0, 9, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 9, 1, 0, 0, 1, 1, 0, 0, 1, 0, + 0, 0, 9, 0, 0, 0, 1, 0, 0, 0, 9, 1, 0, 0, 1, 1, + 0, 1, 0, 0, 0, 9, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 9, 0, 0, 9, 9, 9, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 9, 0, 1, 9, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 9, 9, 1, 1, 0, + 1, 1, 9, 9, 9, 1, 1, 0, 1, 1, 1, 9, 1, 1, 1, 1, + 1, 1, 1, 1 + }; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + // private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + // private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + private int fTokenCount = 0; + + // required holders for white-space compacting + private boolean fShouldLoadBuffered = false; + private String fBufferedContext = null; + private int fBufferedStart = 1; + private int fBufferedLength = 0; + private String f_context = null; + + // state stack for handling embedded regions + private IntStack fStateStack = new IntStack(); + + private String context = null; + private int start = 0; + private int textLength = 0; + private int length = 0; + + // offset for tracking position specific block tags + private int fOffset = 0; + + // the name of the current tag being opened + private String fCurrentTagName = null; + + // the list of tag name BlockMarkers + private List fBlockMarkers = new ArrayList(); + + // required to not seek text blocks on an end tag + private boolean fIsBlockingEnabled = false; + private boolean fIsCaseSensitiveBlocking = true; + + private XMLParserRegionFactory fRegionFactory = new XMLParserRegionFactory(); +/** + * user method + */ +public final void addBlockMarker(BlockMarker marker) { + if(containsTagName(marker.getTagName())) + return; + fBlockMarkers.add(marker); +} +/** + * user method + */ +public final void removeBlockMarker(BlockMarker marker) { + fBlockMarkers.remove(marker); +} +/** + * user method + */ +public final void removeBlockMarker(String tagname) { + if (fBlockMarkers != null) { + Iterator blocks = fBlockMarkers.iterator(); + while (blocks.hasNext()) { + if (((BlockMarker) blocks.next()).getTagName().equals(tagname)) + blocks.remove(); + } + } +} +/* user method */ +public final boolean isCaseSensitiveBlocking() { + return fIsCaseSensitiveBlocking; +} +/* user method */ +public final void setCaseSensitiveBlocking(boolean newValue) { + fIsCaseSensitiveBlocking = newValue; +} +/* user method */ +public boolean getBlockMarkerCaseSensitivity() { + return getBlockMarkerCaseSensitivity(fCurrentTagName); +} +/* user method */ +public boolean getBlockMarkerCaseSensitivity(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if(casesensitive && marker.getTagName().equals(name)) + return casesensitive; + else if(!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return casesensitive; + } + return true; +} +/* user method */ +public String getBlockMarkerContext() { + return getBlockMarkerContext(fCurrentTagName); +} +/* user method */ +public String getBlockMarkerContext(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + if(marker.getTagName().equals(name)) + return marker.getContext(); + } + return BLOCK_TEXT; +} +/* user method */ +public List getBlockMarkers() { + return fBlockMarkers; +} +/* user method */ +public final int getOffset() { + return fOffset + yychar; +} +private final boolean isBlockMarker() { + return isBlockMarker(fCurrentTagName); +} +private final boolean isBlockMarker(String tagName) { + if (!fIsBlockingEnabled) + return false; + return containsTagName(tagName); +} +/** + * user method + */ +public final void beginBlockTagScan(String newTagName) { + beginBlockMarkerScan(newTagName, BLOCK_TEXT); +} +/** + * user method + * + * Special tokenizer setup. Allows tokenization to be initiated at the + * start of a text block within a "newTagName" tag. + * + * Example: + * Tokenizer toker = new Tokenizer(); + * toker.setCaseSensitiveBlocking(false); + * toker.reset(new java.io.StringReader("afiuhqwkejhtasihgalkwhtq</scripter></scr></script>asgdasga")); + * toker.beginBlockMarkerScan("script", BLOCK_TEXT); + * toker.getRegions(); + * + * Returns: + * BLOCK_TEXT: 0-40 + * XML_END_TAG_OPEN: 41-42 + * XML_TAG_NAME: 43-48 + * XML_TAG_CLOSE: 49-49 + * XML_CONTENT: 50-57 + * + */ +public final void beginBlockMarkerScan(String newTagName, String blockcontext) { + yybegin(ST_BLOCK_TAG_SCAN); + fCurrentTagName = newTagName; +} +/** + * Method doScan. + * + * Returns a context region for all of the text from the current position upto the end of input or + * to right *before* the first occurence of searchString + * + * @param searchString - target string to search for ex.: "-->", "</tagname" + * @param requireTailSeparator - whether the target must be immediately followed by whitespace or '>' + * @param context - the context of the scanned region if non-zero length + * @param exitState - the state to go to if the region was of non-zero length + * @param abortState - the state to go to if the searchString was found immediately + * @return String - the context found: the desired context on a non-zero length match, the abortContext on immediate success + * @throws IOException + */ +private final String doScan(String searchString, boolean requireTailSeparator, String searchContext, int exitState, int immediateFallbackState) throws IOException { + boolean stillSearching = true; + // Disable further block (probably) + fIsBlockingEnabled = false; + int searchStringLength = searchString.length(); + int n = 0; + char lastCheckChar; + int i; + boolean same = false; + while (stillSearching) { + n = 0; + // Ensure that enough data from the input exists to compare against the search String. + n = yy_advance(); + while(n != YYEOF && yy_currentPos < searchStringLength) + n = yy_advance(); + // If the input was too short or we've exhausted the input, stop immediately. + if (n == YYEOF) { + stillSearching = false; + } + else { + same = true; + // Ensure that we've not encountered a complete block (<%%>) that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first scan would have come out this far anyway]. + // Check the characters in the target versus the last targetLength characters read from the buffer + // and see if it matches + + // safety check for array accesses (yy_currentPos is the *last* character we can check against) + if(yy_currentPos >= searchStringLength && yy_currentPos <= yy_buffer.length) { + for(i = 0; i < searchStringLength; i++) { + if(same && fIsCaseSensitiveBlocking) + same = yy_buffer[i + yy_currentPos - searchStringLength] == searchString.charAt(i); + else if(same && !fIsCaseSensitiveBlocking) + same = Character.toLowerCase(yy_buffer[i + yy_currentPos - searchStringLength]) == Character.toLowerCase(searchString.charAt(i)); + } + } + // safety check failed; no match is possible right now + else { + same = false; + } + if (same && requireTailSeparator && yy_currentPos < yy_buffer.length) { + // Additional check for close tags to ensure that targetString="</script" doesn't match + // "</scriptS" + lastCheckChar = yy_buffer[yy_currentPos]; + // Succeed on "</script>" and "</script " + if(lastCheckChar == '>' || Character.isWhitespace(lastCheckChar)) + stillSearching = false; + } + else { + stillSearching = !same || (yy_currentPos < yy_startRead + searchStringLength); + } + } + } + if (n != YYEOF || same) { + // We've stopped short of the end or definitely found a match + yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + // If the searchString occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if (yy_markedPos == yy_startRead) { + yybegin(immediateFallbackState); + return primGetNextToken(); + } + } + else { + // We ran through the rest of the input + yy_markedPos = yy_currentPos; + yy_currentPos++; + } + yybegin(exitState); + // If the ending occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if(yy_markedPos == yy_startRead) + return primGetNextToken(); + return searchContext; +} +/** + * user method + * + * A generic lookahead-like operation + */ +private final String doBlockScan(String target, String targetContext, int immediateFallbackState) throws IOException { + return doScan(target, false, targetContext, immediateFallbackState, immediateFallbackState); +} +/** + * user method + * does a lookahead for the current tag name + */ +private final String doBlockTagScan() throws IOException { + fIsCaseSensitiveBlocking = getBlockMarkerCaseSensitivity(); + return doScan("</" + fCurrentTagName, true, getBlockMarkerContext(fCurrentTagName), YYINITIAL, YYINITIAL); +} +/** + * user method + * + * Converts the raw context String returned by the primGetNextToken() + * method into a full ITextRegion by pulling in values for the + * current offset within the scanning text. + * + * Returns null when EOF is encountered and attaches intermittently + * discovered whitespace onto the end of useful regions. + * + * Note that this algorithm caches the token following the one being returned + * so that whitespace can be collapsed. + */ +public final ITextRegion getNextToken() throws IOException { + // load the starting non-whitespace token (assume that it is so) + if (fShouldLoadBuffered) { + context = fBufferedContext; + start = fBufferedStart; + textLength = length = fBufferedLength; + fShouldLoadBuffered = false; + } + else { + context = primGetNextToken(); + if (context == XML_TAG_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } + else if (context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } + else if (context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + start = yychar; + textLength = length = yylength(); + if (yy_atEOF) { + fTokenCount++; + return null; + } + } + // store the next token + f_context = primGetNextToken(); + if (f_context == XML_TAG_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } + else if (f_context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } + else if (f_context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + fBufferedContext = f_context; + fBufferedStart = yychar; + fBufferedLength = yylength(); + fShouldLoadBuffered = true; + if (fBufferedContext == WHITE_SPACE) { + fShouldLoadBuffered = false; + length += fBufferedLength; + } + if (context == null) { + // EOF + if (Debug.debugTokenizer) { + System.out.println(getClass().getName() + " discovered " + fTokenCount + " tokens."); //$NON-NLS-2$//$NON-NLS-1$ + } + return null; + } + fTokenCount++; + return fRegionFactory.createToken(context, start, textLength, length, null, fCurrentTagName); +} +/* user method */ +public XMLTokenizer(){ + super(); +} +/* user method */ +public XMLTokenizer(char[] charArray){ + this(new CharArrayReader(charArray)); +} +/* user method */ +public void reset(char[] charArray) { + reset(new CharArrayReader(charArray), 0); +} +/* user method */ +public void reset(char[] charArray, int newOffset) { + reset(new CharArrayReader(charArray), newOffset); +} +/* user method */ +public void reset(java.io.InputStream in) { + reset(new java.io.InputStreamReader(in), 0); +} +/* user method */ +public void reset(java.io.InputStream in, int newOffset) { + reset(new java.io.InputStreamReader(in), newOffset); +} +/* user method */ +public void reset(java.io.Reader in) { + reset(in, 0); +} +/** + * user method * + * + * Reset internal counters and vars to "newly created" values, in the hopes + * that resetting a pre-existing tokenizer is faster than creating a new one. + * + * This method contains code blocks that were essentially duplicated from the + * <em>generated</em> output of this specification before this method was + * added. Those code blocks were under the above copyright. + */ +public void reset(java.io.Reader in, int newOffset) { + if (Debug.debugTokenizer) { + System.out.println("resetting tokenizer");//$NON-NLS-1$ + } + fOffset = newOffset; + + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + /* user vars: */ + fTokenCount = 0; + + fShouldLoadBuffered = false; + fBufferedContext = null; + fBufferedStart = 1; + fBufferedLength = 0; + fStateStack = new IntStack(); + + context = null; + start = 0; + textLength = 0; + length = 0; +} + + /** + * user method + * + */ + public BlockTokenizer newInstance() { + XMLTokenizer newInstance = new XMLTokenizer(); + // global tagmarkers can be shared; they have no state and + // are never destroyed (e.g. 'release') + for(int i = 0; i < fBlockMarkers.size(); i++) { + BlockMarker blockMarker = (BlockMarker) fBlockMarkers.get(i); + if(blockMarker.isGlobal()) + newInstance.addBlockMarker(blockMarker); + } + return newInstance; + } +/* user method */ +private final String scanXMLCommentText() throws IOException { + // Scan for '-->' and return the text up to that point as + // XML_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_XML_COMMENT_END state and return the next + // context as usual. + return doScan("-->", false, XML_COMMENT_TEXT, ST_XML_COMMENT_END, ST_XML_COMMENT_END); +} + + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ + public XMLTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. + * There is also java.io.Reader version of this constructor. + * + * @param in the java.io.Inputstream to read input from. + */ + public XMLTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed DFA transition table. + * + * @param packed the packed transition table + * @return the unpacked transition table + */ + private static int [] yy_unpack(String packed) { + int [] trans = new int[11055]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 3734) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do trans[j++] = value; while (--count > 0); + } + return trans; + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed the packed character translation table + * @return the unpacked character translation table + */ + private static char [] yy_unpack_cmap(String packed) { + char [] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 1372) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do map[j++] = value; while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning - from the SED JFlex skeleton + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + Logger.log(Logger.ERROR, YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + Logger.log(Logger.ERROR, YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + // DO NOT EXIT the VM on an error + // System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + /** + * user method - skeleton.sed + */ + protected final boolean containsTagName(char[] markerTagName, int offset, int tagnameLength) { + for(int j = 0; j < fBlockMarkers.size(); j++) { + BlockMarker marker = (BlockMarker)fBlockMarkers.get(j); + if(marker.getTagName().length() == tagnameLength) { + boolean matchesSoFar = true; + for(int i = 0; i < tagnameLength && matchesSoFar; i++) { + if(marker.isCaseSensitive()) { + if(marker.getTagName().charAt(i) != markerTagName[i + offset]) + matchesSoFar = false; + } + else { + if(Character.toLowerCase(marker.getTagName().charAt(i)) != Character.toLowerCase(markerTagName[i + offset])) + matchesSoFar = false; + } + } + if(matchesSoFar) + return true; + } + } + return false; + } + + /** + * user method - skeleton.sed + * + * Return ALL of the regions scannable within the remaining text + * Note: for verification use + */ + public final List getRegions() { + List tokens = new ArrayList(); + ITextRegion region = null; + try { + region = getNextToken(); + while(region != null) { + if (region != null) { + tokens.add(region); + } + region = getNextToken(); + } + } + catch (StackOverflowError e) { + Logger.logException(getClass().getName()+": input could not be tokenized correctly at position " + getOffset(), e);//$NON-NLS-1$ + throw e; + } + catch (Exception e) { + // Since this is convenience method and NOT the recommended + // way of getting tokens, many errors are simply hidden + Logger.logException("Exception not handled retrieving regions: " + e.getLocalizedMessage(), e);//$NON-NLS-1$ + } + return tokens; + } + /** + * user method - skeleton.sed + */ + private final void dump(String s) { + if (Debug.debugTokenizer) { + System.out.println(s + " (" + yychar + "-" + //$NON-NLS-2$//$NON-NLS-1$ + (yylength() + yychar) + "):\'" +//$NON-NLS-1$ + StringUtils.escape(yytext()) + "\'");//$NON-NLS-1$ + } + } + /* user method - skeleton.sed */ + public final boolean isEOF() { + return yy_atEOF; + } +/* user method - skeleton.sed */ +protected final boolean containsTagName(String markerTagName) { + Iterator blocks = fBlockMarkers.iterator(); + while(blocks.hasNext()) { + BlockMarker marker = (BlockMarker)blocks.next(); + if(marker.isCaseSensitive()) { + if(marker.getTagName().equals(markerTagName)) + return true; + } + else { + if(marker.getTagName().equalsIgnoreCase(markerTagName)) + return true; + } + } + return false; +} + + /** + * Contains user EOF-code, which will be executed exactly once, + * when the end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + // do nothing, this is the downstream parser's job + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + + while (true) { + + yychar+= yylength(); + + boolean yy_counted = false; + for (yy_currentPos = yy_startRead; yy_currentPos < yy_markedPos; + yy_currentPos++) { + switch (yy_buffer[yy_currentPos]) { + case '\r': + yyline++; + yy_counted = true; + break; + case '\n': + if (yy_counted) + yy_counted = false; + else { + yyline++; + } + break; + default: + yy_counted = false; + } + } + + if (yy_counted) { + if ( yy_advance() == '\n' ) yyline--; + if ( !yy_atEOF ) yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + yy_state = yy_lexical_state; + + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + + int yy_next = yytrans[ yy_rowMap[yy_state] + yycmap[yy_input] ]; + if (yy_next == -1) break yy_forAction; + yy_state = yy_next; + + int yy_attributes = YY_ATTRIBUTE[yy_state]; + if ( (yy_attributes & 1) > 0 ) { + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if ( (yy_attributes & 8) > 0 ) break yy_forAction; + } + + } + } + + + switch (yy_action) { + + case 291: + { + if(Debug.debugTokenizer) + dump("XSL processing instruction target");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 293: break; + case 283: + case 285: + case 286: + { + if(Debug.debugTokenizer) + dump("\nCDATA start");//$NON-NLS-1$ + fStateStack.push(yystate()); + yybegin(ST_CDATA_TEXT); + return XML_CDATA_OPEN; + } + case 294: break; + case 276: + { + if(Debug.debugTokenizer) + dump("element");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION); + return XML_ELEMENT_DECLARATION; + } + case 295: break; + case 275: + { + if(Debug.debugTokenizer) + dump("attlist");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION); + return XML_ATTLIST_DECLARATION; + } + case 296: break; + case 274: + { + if(Debug.debugTokenizer) + dump("doctype");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_DECLARATION); + return XML_DOCTYPE_DECLARATION; + } + case 297: break; + case 268: + { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_ID_PUBLIC); + return XML_DOCTYPE_EXTERNAL_ID_PUBLIC; + } + case 298: break; + case 267: + { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_SYSTEM; + } + case 299: break; + case 263: + case 273: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction target");//$NON-NLS-1$ + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 300: break; + case 237: + { + if(Debug.debugTokenizer) + dump("\nCharRef");//$NON-NLS-1$ + return XML_CHAR_REFERENCE; + } + case 301: break; + case 234: + { + if(Debug.debugTokenizer) + dump("\ncomment start");//$NON-NLS-1$ + yybegin(ST_XML_COMMENT); + return XML_COMMENT_OPEN; + } + case 302: break; + case 215: + case 216: + { + if(Debug.debugTokenizer) + dump("XML processing instruction target");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 303: break; + case 214: + { + if(Debug.debugTokenizer) + dump("comment end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_COMMENT_CLOSE; + } + case 304: break; + case 213: + { + if(Debug.debugTokenizer) + dump("CDATA end");//$NON-NLS-1$ + yybegin(fStateStack.pop()); + return XML_CDATA_CLOSE; + } + case 305: break; + case 212: + { + if(Debug.debugTokenizer) + dump("\nPEReference");//$NON-NLS-1$ + return XML_PE_REFERENCE; + } + case 306: break; + case 209: + { + if(Debug.debugTokenizer) + dump("\nEntityRef");//$NON-NLS-1$ + return XML_ENTITY_REFERENCE; + } + case 307: break; + case 160: + case 174: + case 182: + { + return XML_DOCTYPE_INTERNAL_SUBSET; + } + case 308: break; + case 148: + { + yybegin(YYINITIAL); + if(Debug.debugTokenizer) + dump("empty tag close");//$NON-NLS-1$ + return XML_EMPTY_TAG_CLOSE; + } + case 309: break; + case 133: + { + if(Debug.debugTokenizer) + dump("XML processing instruction end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 310: break; + case 132: + { + // ended with nothing inside + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 311: break; + case 128: + { + if(Debug.debugTokenizer) + dump("processing instruction end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 312: break; + case 120: + { + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("\ndeclaration start");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION); + return XML_DECLARATION_OPEN; + } + case 313: break; + case 119: + { + if(Debug.debugTokenizer) + dump("\nprocessing instruction start");//$NON-NLS-1$ + yybegin(ST_PI); + return XML_PI_OPEN; + } + case 314: break; + case 63: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 315: break; + case 57: + case 59: + case 60: + case 61: + case 137: + { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute value");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 316: break; + case 56: + { + if(Debug.debugTokenizer) + dump("XML processing instruction '='");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 317: break; + case 55: + { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_XML_PI_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 318: break; + case 51: + case 52: + case 53: + { + // block scan until close is found + return doScan("?>", false, XML_PI_CONTENT, ST_XML_PI_TAG_CLOSE, ST_XML_PI_TAG_CLOSE); + } + case 319: break; + case 50: + { + yybegin(ST_PI_CONTENT); + return WHITE_SPACE; + } + case 320: break; + case 46: + case 47: + case 48: + case 49: + case 129: + case 130: + case 131: + case 217: + case 239: + case 240: + case 252: + case 253: + case 262: + case 272: + case 280: + case 284: + case 287: + case 288: + case 289: + case 290: + { + if(Debug.debugTokenizer) + dump("processing instruction target");//$NON-NLS-1$ + yybegin(ST_PI_WS); + return XML_TAG_NAME; + } + case 321: break; + case 41: + case 42: + { + if(Debug.debugTokenizer) + dump("comment content");//$NON-NLS-1$ + return scanXMLCommentText(); + } + case 322: break; + case 40: + { + if(Debug.debugTokenizer) + dump("LINE FEED");//$NON-NLS-1$ + return WHITE_SPACE; + } + case 323: break; + case 0: + case 31: + case 122: + case 124: + case 207: + case 208: + case 236: + { + if(Debug.debugTokenizer) + dump("\nXML content");//$NON-NLS-1$ + return XML_CONTENT; + } + case 324: break; + case 5: + case 8: + case 9: + case 10: + case 12: + case 13: + case 14: + case 15: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 28: + case 45: + { + if(Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + return WHITE_SPACE; + } + case 325: break; + case 16: + case 71: + { + if(Debug.debugTokenizer) + dump("inappropriate tag name");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_CONTENT; + } + case 326: break; + case 27: + case 106: + case 107: + case 193: + case 229: + case 248: + case 259: + case 269: + case 277: + case 281: + { + if(Debug.debugTokenizer) + dump("elementdecl contentspec");//$NON-NLS-1$ + return XML_ELEMENT_DECL_CONTENT; + } + case 327: break; + case 29: + case 113: + case 114: + case 204: + case 233: + case 250: + case 260: + case 270: + case 278: + case 282: + { + if(Debug.debugTokenizer) + dump("attlist contentspec");//$NON-NLS-1$ + return XML_ATTLIST_DECL_CONTENT; + } + case 328: break; + case 32: + case 72: + case 83: + { + if(Debug.debugTokenizer) + dump("\nstart tag open");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + case 329: break; + case 33: + case 34: + case 37: + case 38: + case 39: + case 43: + case 44: + case 54: + case 58: + case 62: + case 64: + case 68: + case 74: + case 80: + case 85: + case 86: + case 87: + case 88: + case 90: + case 91: + case 93: + case 98: + case 103: + case 110: + { + if (Debug.debugTokenizer) + System.out.println("!!!unexpected!!!: \"" + yytext() + "\":" + //$NON-NLS-2$//$NON-NLS-1$ + yychar + "-" + (yychar + yylength()));//$NON-NLS-1$ + return UNDEFINED; + } + case 330: break; + case 35: + case 36: + { + if(Debug.debugTokenizer) + dump("CDATA text");//$NON-NLS-1$ + String blockContext = doBlockScan("]]>", XML_CDATA_TEXT, ST_CDATA_END);//$NON-NLS-1$ + if(blockContext == XML_CDATA_TEXT) + yybegin(ST_CDATA_END); + return blockContext; + } + case 331: break; + case 65: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_DHTML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 332: break; + case 66: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction '='");//$NON-NLS-1$ + yybegin(ST_DHTML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 333: break; + case 67: + case 69: + case 70: + case 145: + { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute value");//$NON-NLS-1$ + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 334: break; + case 73: + { + if(Debug.debugTokenizer) + dump("tag close");//$NON-NLS-1$ + if(isBlockMarker()) { + yybegin(ST_BLOCK_TAG_SCAN); + } + else + yybegin(YYINITIAL); + return XML_TAG_CLOSE; + } + case 335: break; + case 75: + case 76: + { + if(Debug.debugTokenizer) + dump("tag name");//$NON-NLS-1$ + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 336: break; + case 77: + { + if(Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + yybegin(ST_XML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 337: break; + case 78: + { + if(Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + yybegin(ST_XML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 338: break; + case 79: + case 81: + case 82: + case 152: + { + if(Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 339: break; + case 84: + { + if(Debug.debugTokenizer) + dump("declaration end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 340: break; + case 89: + { + if(Debug.debugTokenizer) + dump("doctype type");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_EXTERNAL_ID); + return XML_DOCTYPE_NAME; + } + case 341: break; + case 92: + case 94: + case 95: + case 96: + case 166: + case 167: + case 170: + case 171: + case 224: + { + if(Debug.debugTokenizer) + dump("doctype public reference");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_PUBREF; + } + case 342: break; + case 97: + case 99: + case 100: + case 101: + case 178: + { + if(Debug.debugTokenizer) + dump("doctype system reference");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION_CLOSE); + return XML_DOCTYPE_EXTERNAL_ID_SYSREF; + } + case 343: break; + case 102: + case 104: + case 105: + case 186: + case 187: + case 190: + case 191: + case 227: + { + if(Debug.debugTokenizer) + dump("elementdecl name");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION_CONTENT); + return XML_ELEMENT_DECL_NAME; + } + case 344: break; + case 108: + { + if(Debug.debugTokenizer) + dump("elementdecl close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 345: break; + case 109: + case 111: + case 112: + case 197: + case 198: + case 201: + case 202: + case 231: + { + if(Debug.debugTokenizer) + dump("attlist name");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION_CONTENT); + return XML_ATTLIST_DECL_NAME; + } + case 346: break; + case 115: + { + if(Debug.debugTokenizer) + dump("attlist close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 347: break; + case 118: + { + if(Debug.debugTokenizer) + dump("\nend tag open");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + return XML_END_TAG_OPEN; + } + case 348: break; + case 116: + case 117: + { + return doBlockTagScan(); + } + case 349: break; + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + return null; + } + else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.jflex b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.jflex new file mode 100644 index 0000000..020e3e7 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.jflex
@@ -0,0 +1,1332 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 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.wst.xml.core.internal.parser; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTokenizer; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.eclipse.wst.xml.core.internal.Logger; +import org.eclipse.wst.xml.core.internal.parser.regions.XMLParserRegionFactory; +import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; + +%% + +%{ + private int fTokenCount = 0; + + // required holders for white-space compacting + private boolean fShouldLoadBuffered = false; + private String fBufferedContext = null; + private int fBufferedStart = 1; + private int fBufferedLength = 0; + private String f_context = null; + + // state stack for handling embedded regions + private IntStack fStateStack = new IntStack(); + + private String context = null; + private int start = 0; + private int textLength = 0; + private int length = 0; + + // offset for tracking position specific block tags + private int fOffset = 0; + + // the name of the current tag being opened + private String fCurrentTagName = null; + + // the list of tag name BlockMarkers + private List fBlockMarkers = new ArrayList(); + + // required to not seek text blocks on an end tag + private boolean fIsBlockingEnabled = false; + private boolean fIsCaseSensitiveBlocking = true; + + private XMLParserRegionFactory fRegionFactory = new XMLParserRegionFactory(); +/** + * user method + */ +public final void addBlockMarker(BlockMarker marker) { + if(containsTagName(marker.getTagName())) + return; + fBlockMarkers.add(marker); +} +/** + * user method + */ +public final void removeBlockMarker(BlockMarker marker) { + fBlockMarkers.remove(marker); +} +/** + * user method + */ +public final void removeBlockMarker(String tagname) { + if (fBlockMarkers != null) { + Iterator blocks = fBlockMarkers.iterator(); + while (blocks.hasNext()) { + if (((BlockMarker) blocks.next()).getTagName().equals(tagname)) + blocks.remove(); + } + } +} +/* user method */ +public final boolean isCaseSensitiveBlocking() { + return fIsCaseSensitiveBlocking; +} +/* user method */ +public final void setCaseSensitiveBlocking(boolean newValue) { + fIsCaseSensitiveBlocking = newValue; +} +/* user method */ +public boolean getBlockMarkerCaseSensitivity() { + return getBlockMarkerCaseSensitivity(fCurrentTagName); +} +/* user method */ +public boolean getBlockMarkerCaseSensitivity(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if(casesensitive && marker.getTagName().equals(name)) + return casesensitive; + else if(!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return casesensitive; + } + return true; +} +/* user method */ +public String getBlockMarkerContext() { + return getBlockMarkerContext(fCurrentTagName); +} +/* user method */ +public String getBlockMarkerContext(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while(iterator.hasNext()) { + BlockMarker marker = (BlockMarker)iterator.next(); + if(marker.getTagName().equals(name)) + return marker.getContext(); + } + return BLOCK_TEXT; +} +/* user method */ +public List getBlockMarkers() { + return fBlockMarkers; +} +/* user method */ +public final int getOffset() { + return fOffset + yychar; +} +private final boolean isBlockMarker() { + return isBlockMarker(fCurrentTagName); +} +private final boolean isBlockMarker(String tagName) { + if (!fIsBlockingEnabled) + return false; + return containsTagName(tagName); +} +/** + * user method + */ +public final void beginBlockTagScan(String newTagName) { + beginBlockMarkerScan(newTagName, BLOCK_TEXT); +} +/** + * user method + * + * Special tokenizer setup. Allows tokenization to be initiated at the + * start of a text block within a "newTagName" tag. + * + * Example: + * Tokenizer toker = new Tokenizer(); + * toker.setCaseSensitiveBlocking(false); + * toker.reset(new java.io.StringReader("afiuhqwkejhtasihgalkwhtq</scripter></scr></script>asgdasga")); + * toker.beginBlockMarkerScan("script", BLOCK_TEXT); + * toker.getRegions(); + * + * Returns: + * BLOCK_TEXT: 0-40 + * XML_END_TAG_OPEN: 41-42 + * XML_TAG_NAME: 43-48 + * XML_TAG_CLOSE: 49-49 + * XML_CONTENT: 50-57 + * + */ +public final void beginBlockMarkerScan(String newTagName, String blockcontext) { + yybegin(ST_BLOCK_TAG_SCAN); + fCurrentTagName = newTagName; +} +/** + * Method doScan. + * + * Returns a context region for all of the text from the current position upto the end of input or + * to right *before* the first occurence of searchString + * + * @param searchString - target string to search for ex.: "-->", "</tagname" + * @param requireTailSeparator - whether the target must be immediately followed by whitespace or '>' + * @param context - the context of the scanned region if non-zero length + * @param exitState - the state to go to if the region was of non-zero length + * @param abortState - the state to go to if the searchString was found immediately + * @return String - the context found: the desired context on a non-zero length match, the abortContext on immediate success + * @throws IOException + */ +private final String doScan(String searchString, boolean requireTailSeparator, String searchContext, int exitState, int immediateFallbackState) throws IOException { + boolean stillSearching = true; + // Disable further block (probably) + fIsBlockingEnabled = false; + int searchStringLength = searchString.length(); + int n = 0; + char lastCheckChar; + int i; + boolean same = false; + while (stillSearching) { + n = 0; + // Ensure that enough data from the input exists to compare against the search String. + n = yy_advance(); + while(n != YYEOF && yy_currentPos < searchStringLength) + n = yy_advance(); + // If the input was too short or we've exhausted the input, stop immediately. + if (n == YYEOF) { + stillSearching = false; + } + else { + same = true; + // Ensure that we've not encountered a complete block (<%%>) that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first scan would have come out this far anyway]. + // Check the characters in the target versus the last targetLength characters read from the buffer + // and see if it matches + + // safety check for array accesses (yy_currentPos is the *last* character we can check against) + if(yy_currentPos >= searchStringLength && yy_currentPos <= yy_buffer.length) { + for(i = 0; i < searchStringLength; i++) { + if(same && fIsCaseSensitiveBlocking) + same = yy_buffer[i + yy_currentPos - searchStringLength] == searchString.charAt(i); + else if(same && !fIsCaseSensitiveBlocking) + same = Character.toLowerCase(yy_buffer[i + yy_currentPos - searchStringLength]) == Character.toLowerCase(searchString.charAt(i)); + } + } + // safety check failed; no match is possible right now + else { + same = false; + } + if (same && requireTailSeparator && yy_currentPos < yy_buffer.length) { + // Additional check for close tags to ensure that targetString="</script" doesn't match + // "</scriptS" + lastCheckChar = yy_buffer[yy_currentPos]; + // Succeed on "</script>" and "</script " + if(lastCheckChar == '>' || Character.isWhitespace(lastCheckChar)) + stillSearching = false; + } + else { + stillSearching = !same || (yy_currentPos < yy_startRead + searchStringLength); + } + } + } + if (n != YYEOF || same) { + // We've stopped short of the end or definitely found a match + yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + // If the searchString occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if (yy_markedPos == yy_startRead) { + yybegin(immediateFallbackState); + return primGetNextToken(); + } + } + else { + // We ran through the rest of the input + yy_markedPos = yy_currentPos; + yy_currentPos++; + } + yybegin(exitState); + // If the ending occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if(yy_markedPos == yy_startRead) + return primGetNextToken(); + return searchContext; +} +/** + * user method + * + * A generic lookahead-like operation + */ +private final String doBlockScan(String target, String targetContext, int immediateFallbackState) throws IOException { + return doScan(target, false, targetContext, immediateFallbackState, immediateFallbackState); +} +/** + * user method + * does a lookahead for the current tag name + */ +private final String doBlockTagScan() throws IOException { + fIsCaseSensitiveBlocking = getBlockMarkerCaseSensitivity(); + return doScan("</" + fCurrentTagName, true, getBlockMarkerContext(fCurrentTagName), YYINITIAL, YYINITIAL); +} +/** + * user method + * + * Converts the raw context String returned by the primGetNextToken() + * method into a full ITextRegion by pulling in values for the + * current offset within the scanning text. + * + * Returns null when EOF is encountered and attaches intermittently + * discovered whitespace onto the end of useful regions. + * + * Note that this algorithm caches the token following the one being returned + * so that whitespace can be collapsed. + */ +public final ITextRegion getNextToken() throws IOException { + // load the starting non-whitespace token (assume that it is so) + if (fShouldLoadBuffered) { + context = fBufferedContext; + start = fBufferedStart; + textLength = length = fBufferedLength; + fShouldLoadBuffered = false; + } + else { + context = primGetNextToken(); + if (context == XML_TAG_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } + else if (context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } + else if (context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + start = yychar; + textLength = length = yylength(); + if (yy_atEOF) { + fTokenCount++; + return null; + } + } + // store the next token + f_context = primGetNextToken(); + if (f_context == XML_TAG_NAME) { + if(containsTagName(yy_buffer, yy_startRead, yy_markedPos-yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } + else if (f_context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } + else if (f_context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + fBufferedContext = f_context; + fBufferedStart = yychar; + fBufferedLength = yylength(); + fShouldLoadBuffered = true; + if (fBufferedContext == WHITE_SPACE) { + fShouldLoadBuffered = false; + length += fBufferedLength; + } + if (context == null) { + // EOF + if (Debug.debugTokenizer) { + System.out.println(getClass().getName() + " discovered " + fTokenCount + " tokens."); //$NON-NLS-2$//$NON-NLS-1$ + } + return null; + } + fTokenCount++; + return fRegionFactory.createToken(context, start, textLength, length, null, fCurrentTagName); +} +/* user method */ +public XMLTokenizer(){ + super(); +} +/* user method */ +public XMLTokenizer(char[] charArray){ + this(new CharArrayReader(charArray)); +} +/* user method */ +public void reset(char[] charArray) { + reset(new CharArrayReader(charArray), 0); +} +/* user method */ +public void reset(char[] charArray, int newOffset) { + reset(new CharArrayReader(charArray), newOffset); +} +/* user method */ +public void reset(java.io.InputStream in) { + reset(new java.io.InputStreamReader(in), 0); +} +/* user method */ +public void reset(java.io.InputStream in, int newOffset) { + reset(new java.io.InputStreamReader(in), newOffset); +} +/* user method */ +public void reset(java.io.Reader in) { + reset(in, 0); +} +/** + * user method * + * + * Reset internal counters and vars to "newly created" values, in the hopes + * that resetting a pre-existing tokenizer is faster than creating a new one. + * + * This method contains code blocks that were essentially duplicated from the + * <em>generated</em> output of this specification before this method was + * added. Those code blocks were under the above copyright. + */ +public void reset(java.io.Reader in, int newOffset) { + if (Debug.debugTokenizer) { + System.out.println("resetting tokenizer");//$NON-NLS-1$ + } + fOffset = newOffset; + + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* this buffer contains the current text to be matched and is + the source of the yytext() string */ + java.util.Arrays.fill(yy_buffer, (char)0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + //yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + //yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + /* user vars: */ + fTokenCount = 0; + + fShouldLoadBuffered = false; + fBufferedContext = null; + fBufferedStart = 1; + fBufferedLength = 0; + fStateStack = new IntStack(); + + context = null; + start = 0; + textLength = 0; + length = 0; +} + + /** + * user method + * + */ + public BlockTokenizer newInstance() { + XMLTokenizer newInstance = new XMLTokenizer(); + // global tagmarkers can be shared; they have no state and + // are never destroyed (e.g. 'release') + for(int i = 0; i < fBlockMarkers.size(); i++) { + BlockMarker blockMarker = (BlockMarker) fBlockMarkers.get(i); + if(blockMarker.isGlobal()) + newInstance.addBlockMarker(blockMarker); + } + return newInstance; + } +/* user method */ +private final String scanXMLCommentText() throws IOException { + // Scan for '-->' and return the text up to that point as + // XML_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_XML_COMMENT_END state and return the next + // context as usual. + return doScan("-->", false, XML_COMMENT_TEXT, ST_XML_COMMENT_END, ST_XML_COMMENT_END); +} +%} + +%eof{ +// do nothing, this is the downstream parser's job +%eof} + +%public +%class XMLTokenizer +%implements BlockTokenizer, DOMRegionContext +%function primGetNextToken +%type String +%char +%unicode +%pack + +%state ST_CDATA_TEXT +%state ST_CDATA_END +%state ST_XML_COMMENT +%state ST_XML_COMMENT_END +%state ST_PI +%state ST_PI_WS +%state ST_PI_CONTENT +%state ST_XML_PI_ATTRIBUTE_NAME +%state ST_XML_PI_EQUALS +%state ST_XML_PI_ATTRIBUTE_VALUE +%state ST_XML_PI_TAG_CLOSE +%state ST_DHTML_ATTRIBUTE_NAME +%state ST_DHTML_EQUALS +%state ST_DHTML_ATTRIBUTE_VALUE +%state ST_DHTML_TAG_CLOSE + +// normal tag states +%state ST_XML_TAG_NAME +%state ST_XML_ATTRIBUTE_NAME +%state ST_XML_EQUALS +%state ST_XML_ATTRIBUTE_VALUE + +// declaration (DTD) states +%state ST_XML_DECLARATION +%state ST_XML_DECLARATION_CLOSE + +%state ST_XML_DOCTYPE_DECLARATION +%state ST_XML_DOCTYPE_EXTERNAL_ID +%state ST_XML_DOCTYPE_ID_PUBLIC +%state ST_XML_DOCTYPE_ID_SYSTEM + +%state ST_XML_ELEMENT_DECLARATION +%state ST_XML_ELEMENT_DECLARATION_CONTENT + +%state ST_XML_ATTLIST_DECLARATION +%state ST_XML_ATTLIST_DECLARATION_CONTENT + +%state ST_BLOCK_TAG_SCAN + +// Letter = ([A-Za-z]) +// Digit = ([0-9]) + +/** + * smaller tokens + */ +genericTagOpen = < +genericTagClose = > +genericEndTagOpen = <\/ +genericEmptyTagClose = \/> + +PIstart = <\? +PIend = \?> + + +// [1] document ::= prolog element Misc* +document = ({prolog} {element} {Misc}*) + +// [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + //Char = (.) +Char = [\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD] + +// [3] S ::= (0x20 | 0x9 | 0xD | 0xA)+ +S = [\x20\x09\x0D\x0A]+ + +// [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender +NameChar = ({Letter} | {Digit} | \. | \- | _ | : | {CombiningChar} | {Extender}) + +// [5] Name ::= (Letter | '_' | ':') NameChar* +//Name = ({NameChar}{NameChar}*) +Name = ({Letter} | _ | :){NameChar}* + +// [6] Names ::= {Name} ({S} {Name})* +Names = ({Name} ({S} {Name})*) + +// [7] Nmtoken ::= (NameChar)+ +Nmtoken = ({NameChar}+) + +// [8] Nmtokens ::= Nmtoken (S Nmtoken)* +Nmtokens = ({Nmtoken} ({S} {Nmtoken})*) + +// [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | "'" ([^%&'] | PEReference | Reference)* "'" +EntityValue = (\" ([^%&\"] | {PEReference} | {Reference})* \" | \' ([^%&\'] | {PEReference} | {Reference})* \') + +// [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" +AttValue = ( \" ([^\"] | {Reference})* \" | \' ([^\'] | {Reference})* \' | ([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) + +// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") +SystemLiteral = ((\" [^\"]* \") | (\' [^\']* \')) + +// [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" +PubidLiteral = (\" {PubidChar}* \" | \' ({PubidChar}\')* "'") + +// [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] +PubidChar = ([\040\015\012] | [a-zA-Z0-9] | [\-\'()\+,.\/:=?;!\*#@\$_%]) + +// [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) +// implement lookahead behavior during action definition +CharData = ([^<&(\]\]>)]*) + +// [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' +CommentStart = (<!\-\-) +CommentEnd = (\-\->) +Comment = ({CommentStart}.*{CommentEnd}) + +// [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' +PI = (<\?{PITarget} {Char}* \?>) + +// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) +PITarget = ({Name}((X|x)(M|m)(L|l))) + +// [18] CDSect ::= CDStart CData CDEnd +CDSect = ({CDStart}{CData}{CDEnd}) + +// [19] CDStart ::= '<![CDATA[' +CDStart = <!\[CDATA\[ + +// [20] CData ::= (Char* - (Char* ']]>' Char*)) +// implement lookahead behavior during action definition +CData = ([^(\]\]>)]*) + +// [21] CDEnd ::= ']]>' +CDEnd = (\]\]>) + +// [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? +prolog = ({XMLDecl}? {Misc}* ({doctypedecl} {Misc}*)?) + +// [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' +XMLDecl = (<\?xml {VersionInfo} {EncodingDecl}? {SDDecl}? {S}? \?>) + +// [24] VersionInfo ::= S 'version' Eq (' VersionNum ' | " VersionNum ") +VersionInfo = ({S}version{Eq}(\'{VersionNum}\' | \"{VersionNum}\")) + +// [25] Eq ::= S? '=' S? +Eq = (\=) + +// [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')+ +VersionNum = (([a-zA-Z0-9_.:]|\-)+) + +// [27] Misc ::= Comment | PI | S +Misc = ({Comment} | {PI} | {S}) + +// [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' (markupdecl | PEReference | S)* ']' S?)? '>' +doctypedecl = (<\!DOCTYPE{S}{Name} ({S}{ExternalID})? {S}? (\[ ({markupdecl}|{PEReference}|{S})* \]{S}?)?>) + +// [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment +markupdecl = ({elementdecl} | {AttlistDecl} | {EntityDecl} | {NotationDecl} | {PI} | {Comment}) + +// [30] extSubset ::= TextDecl? extSubsetDecl +extSubset = ({TextDecl}? {extSubsetDecl}) + +// [31] extSubsetDecl ::= ( markupdecl | conditionalSect | PEReference | S )* +extSubsetDecl = (( {markupdecl} | {conditionalSect} | {PEReference} | {S} )*) + +// [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) +SDDecl = ({S}standalone{Eq}{S}*((\'(yes|no)\')|(\"(yes|no)\"))) + +// [33] LanguageID ::= Langcode ('-' Subcode)* +LanguageID = ({Langcode}(\-{Subcode})*) + +// [34] Langcode ::= ISO639Code | IanaCode | UserCode +Langcode = ({ISO639Code} | {IanaCode} | {UserCode}) + +// [35] ISO639Code ::= ([a-z] | [A-Z]) ([a-z] | [A-Z]) +ISO639Code = (([a-z]|[A-Z])([a-z]|[A-Z])) + +// [36] IanaCode ::= ('i' | 'I') '-' ([a-z] | [A-Z])+ +IanaCode = ((i|I)\-([a-z]|[A-Z])+) + +// [37] UserCode ::= ('x' | 'X') '-' ([a-z] | [A-Z])+ +UserCode = ((x|X)\-([a-z]|[A-Z])+) + +// [38] Subcode ::= ([a-z] | [A-Z])+ +Subcode = (([a-z]|[A-Z])+) + +// [39] element ::= EmptyElemTag | STag content ETag +element = ({EmptyElemTag} | {STag} {content} {ETag}) + +// [40] STag ::= '<' Name (S Attribute)* S? '>' +STag = (<{Name}({S}{Attribute})*{S}?>) + +// [41] Attribute ::= Name Eq AttValue +Attribute = ({Name}{S}*{Eq}{S}*{AttValue}) + +// [42] ETag ::= 'Name S? '>' +ETag = (<\/{Name}{S}?>) + +// [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)* +content = (({element} | {CharData} | {Reference} | {CDSect} | {PI} | {Comment})*) + +// [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' +EmptyElemTag = (<{Name}({S}{Attribute})*{S}?\/>) + +// [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>' +elementdecl = (<\!ELEMENT{S}{Name}{S}{contentspec}{S}?>) + +// [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children +contentspec = (EMPTY|ANY|{Mixed}|{children}) + +// [47] children ::= (choice | seq) ('?' | '*' | '+')? +children = (({choice}|{seq})(\?|\*|\+)?) + +// CAUSES LOOP THROUGH DEFS OF CHOICE AND SEQ +// [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')? +cp = (({Name} | {choice} | {seq}) (\?|\*|\+)?) + +// [49] choice ::= '(' S? cp ( S? '|' S? cp )* S? ')' +// choice = \({S}?{cp}({S}?\|{S}?{cp})*{S}?\) +choice = \({S}?{Name}({S}?\|{S}?{Name})*{S}?\) + +// [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' +// seq = (\({S}?{cp}({S}?\,{S}?{cp})*{S}?\)) +seq = (\({S}?{Name}({S}?\,{S}?{Name})*{S}?\)) + +// [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' +Mixed = ({S}?\#PCDATA({S}?\|{S}?{Name})*{S}?)*\|({S}?\#PCDATA{S}?) + +// [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>' +AttlistDecl = (<\!ATTLIST{S}{Name}{AttDef}*{S}?>) + +// [53] AttDef ::= S Name S AttType S DefaultDecl +AttDef = ({S}{Name}{S}{AttType}{S}{DefaultDecl}) + +// [54] AttType ::= StringType | TokenizedType | EnumeratedType +AttType = ({StringType} | {TokenizedType} | {EnumeratedType}) + +// [55] StringType ::= 'CDATA' +StringType = (CDATA) + +// [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS' +TokenizedType = (ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS) + +// [57] EnumeratedType ::= NotationType | Enumeration +EnumeratedType = ({NotationType} | {Enumeration}) + +// [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' +NotationType = (NOTATION{S}\({S}?{Name}({S}?\|{S}?{Name})*{S}?\)) + +// [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' +Enumeration = (\({S}?{Nmtoken}({S}?\|{S}?{Nmtoken})*{S}?\)) + +// [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) +DefaultDecl = (\#REQUIRED|\#IMPLIED|((\#FIXED{S})?{AttValue})) + +// [61] conditionalSect ::= includeSect | ignoreSect +conditionalSect = ({includeSect} | {ignoreSect}) + +// [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>' +includeSect = (<\!\[{S}?INCLUDE{S}?\[{extSubsetDecl}\]\]>) + +// [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>' +ignoreSect = (<\!\[{S}?IGNORE{S}?\[{ignoreSectContents}*\]\]>) + +// [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)* +ignoreSectContents = ({Ignore}(<\!\[{ignoreSectContents}\]\]>{Ignore})*) + +// [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*) +Ignore = ([^(\<\!\[|\]\]\>)]*) + +// [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';' +CharRef = (&#[0-9]+;|&#x[0-9a-fA-F]+;) + +// [67] Reference ::= EntityRef | CharRef +Reference = ({EntityRef} | {CharRef}) + +// [68] EntityRef = '&' Name ';' +EntityRef = (&{Name};) + +// [69] PEReference ::= '%' Name ';' +PEReference = (%{Name};) + +// [70] EntityDecl ::= GEDecl | PEDecl +EntityDecl = ({GEDecl} | {PEDecl}) + +// [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>' +GEDecl = (<\!ENTITY{S}{Name}{S}{EntityDef}{S}?>) + +// [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>' +PEDecl = (<\!ENTITY{S}\%{S}{Name}{S}{PEDef}{S}?>) + +// [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) +EntityDef = ({EntityValue} | ({ExternalID}{NDataDecl}?)) + +// [74] PEDef ::= EntityValue | ExternalID +PEDef = ({EntityValue} | {ExternalID}) + +// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral +ExternalID = (SYSTEM{S}{SystemLiteral}|PUBLIC{S}{PubidLiteral}{S}{SystemLiteral} ) + +// [76] NDataDecl ::= S 'NDATA' S Name +NDataDecl = ({S}NDATA{S}{Name}) + +// [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>' +TextDecl = (<\?xml{VersionInfo}?{EncodingDecl}{S}?\?>) + +// [78] extParsedEnt ::= TextDecl? content +extParsedEnt = ({TextDecl}?{content}) + +// [79] extPE ::= TextDecl? extSubsetDecl +extPE = ({TextDecl}?{extSubsetDecl}) + +// [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) +EncodingDecl = ({S}encoding{S}*{Eq}{S}*(\"{EncName}\"|\'{EncName}\')) + +// [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* +EncName = ([A-Za-z]([A-Za-z0-9._]|\-)*) + +// [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>' +NotationDecl = (<\!NOTATION{S}{Name}{S}({ExternalID}|{PublicID}){S}?>) + +// [83] PublicID ::= 'PUBLIC' S PubidLiteral +PublicID = (PUBLIC{S}{PubidLiteral}) + +// [84] Letter ::= BaseChar | Ideographic +Letter = ({BaseChar} | {Ideographic}) + +// [85] BaseChar ::= [#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] +// | [#x00D8-#x00F6] | [#x00F8-#x00FF] | [#x0100-#x0131] +// | [#x0134-#x013E] | [#x0141-#x0148] | [#x014A-#x017E] +// | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] +// | [#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 +// | [#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] +// | [#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 +// | [#x03E2-#x03F3] | [#x0401-#x040C] | [#x040E-#x044F] +// | [#x0451-#x045C] | [#x045E-#x0481] | [#x0490-#x04C4] +// | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] +// | [#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 +// | [#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] +// | [#x0621-#x063A] | [#x0641-#x064A] | [#x0671-#x06B7] +// | [#x06BA-#x06BE] | [#x06C0-#x06CE] | [#x06D0-#x06D3] | #x06D5 +// | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | [#x0958-#x0961] +// | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] +// | [#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] +// | [#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] +// | [#x0A0F-#x0A10] | [#x0A13-#x0A28] | [#x0A2A-#x0A30] +// | [#x0A32-#x0A33] | [#x0A35-#x0A36] | [#x0A38-#x0A39] +// | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | [#x0A85-#x0A8B] +// | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | [#x0AAA-#x0AB0] +// | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 +// | [#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] +// | [#x0B2A-#x0B30] | [#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D +// | [#x0B5C-#x0B5D] | [#x0B5F-#x0B61] | [#x0B85-#x0B8A] +// | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | [#x0B99-#x0B9A] | #x0B9C +// | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | [#x0BA8-#x0BAA] +// | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] +// | [#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] +// | [#x0C35-#x0C39] | [#x0C60-#x0C61] | [#x0C85-#x0C8C] +// | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] | [#x0CAA-#x0CB3] +// | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | [#x0D05-#x0D0C] +// | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] +// | [#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] +// | [#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | [#x0E87-#x0E88] +// | #x0E8A | #x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] +// | [#x0EA1-#x0EA3] | #x0EA5 | #x0EA7 | [#x0EAA-#x0EAB] +// | [#x0EAD-#x0EAE] | #x0EB0 | [#x0EB2-#x0EB3] | #x0EBD +// | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] +// | [#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] +// | [#x1105-#x1107] | #x1109 | [#x110B-#x110C] | [#x110E-#x1112] +// | #x113C | #x113E | #x1140 | #x114C | #x114E | #x1150 +// | [#x1154-#x1155] | #x1159 | [#x115F-#x1161] | #x1163 | #x1165 +// | #x1167 | #x1169 | [#x116D-#x116E] | [#x1172-#x1173] | #x1175 +// | #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | [#x11B7-#x11B8] +// | #x11BA | [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 +// | [#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] +// | [#x1F18-#x1F1D] | [#x1F20-#x1F45] | [#x1F48-#x1F4D] +// | [#x1F50-#x1F57] | #x1F59 | #x1F5B | #x1F5D | [#x1F5F-#x1F7D] +// | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | [#x1FC2-#x1FC4] +// | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] +// | [#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 +// | [#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] +// | [#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3] +BaseChar = [\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3] + +// [86] Ideographic ::= [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029] +Ideographic = [\u4E00-\u9FA5\u3007\u3021-\u3029] + +// [87] CombiningChar ::= [#x0300-#x0345] | [#x0360-#x0361] +// | [#x0483-#x0486] | [#x0591-#x05A1] | [#x05A3-#x05B9] +// | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 +// | [#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] +// | [#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] +// | [#x0901-#x0903] | #x093C | [#x093E-#x094C] | #x094D +// | [#x0951-#x0954] | [#x0962-#x0963] | [#x0981-#x0983] | #x09BC +// | #x09BE | #x09BF | [#x09C0-#x09C4] | [#x09C7-#x09C8] +// | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | #x0A3C +// | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] +// | [#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC +// | [#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] +// | [#x0B01-#x0B03] | #x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] +// | [#x0B4B-#x0B4D] | [#x0B56-#x0B57] | [#x0B82-#x0B83] +// | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | [#x0BCA-#x0BCD] | #x0BD7 +// | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | [#x0C46-#x0C48] +// | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] +// | [#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] +// | [#x0CD5-#x0CD6] | [#x0D02-#x0D03] | [#x0D3E-#x0D43] +// | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | #x0D57 | #x0E31 +// | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | [#x0EB4-#x0EB9] +// | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | #x0F35 +// | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] +// | [#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] +// | [#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 +// | [#x302A-#x302F] | #x3099 | #x309A +CombiningChar = [\u0300-\u0345\u0360-\u0361\u0483-\u0486\u0591-\u05A1\u05A3-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4\u064B-\u0652\u0670\u06D6-\u06DC\u06DD-\u06DF\u06E0-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0901-\u0903\u093C\u093E-\u094C\u094D\u0951-\u0954\u0962-\u0963\u0981-\u0983\u09BC\u09BE\u09BF\u09C0-\u09C4\u09C7-\u09C8\u09CB-\u09CD\u09D7\u09E2-\u09E3\u0A02\u0A3C\u0A3E\u0A3F\u0A40-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A70-\u0A71\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0B01-\u0B03\u0B3C\u0B3E-\u0B43\u0B47-\u0B48\u0B4B-\u0B4D\u0B56-\u0B57\u0B82-\u0B83\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C82-\u0C83\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5-\u0CD6\u0D02-\u0D03\u0D3E-\u0D43\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86-\u0F8B\u0F90-\u0F95\u0F97\u0F99-\u0FAD\u0FB1-\u0FB7\u0FB9\u20D0-\u20DC\u20E1\u302A-\u302F\u3099\u309A] + +// [88] Digit ::= [#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] +// | [#x0966-#x096F] | [#x09E6-#x09EF] | [#x0A66-#x0A6F] +// | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | [#x0BE7-#x0BEF] +// | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] +// | [#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29] +Digit = [\u0030-\u0039\u0660-\u0669\u06F0-\u06F9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE7-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29] + +// [89] Extender ::= #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 +// | #x0EC6 | #x3005 | [#x3031-#x3035] | [#x309D-#x309E] +// | [#x30FC-#x30FE] +Extender = [\u00B7\u02D0\u02D1\u0387\u0640\u0E46\u0EC6\u3005\u3031-\u3035\u309D-\u309E\u30FC-\u30FE] + +%% + + +/* white space within a tag */ +<ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_PI, ST_XML_PI_EQUALS, ST_XML_PI_ATTRIBUTE_NAME, ST_XML_PI_ATTRIBUTE_VALUE, ST_XML_DECLARATION, ST_XML_DOCTYPE_DECLARATION, ST_XML_ELEMENT_DECLARATION, ST_XML_ATTLIST_DECLARATION, ST_XML_DECLARATION_CLOSE, ST_XML_DOCTYPE_ID_PUBLIC, ST_XML_DOCTYPE_ID_SYSTEM, ST_XML_DOCTYPE_EXTERNAL_ID,ST_DHTML_ATTRIBUTE_NAME,ST_DHTML_EQUALS,ST_DHTML_ATTRIBUTE_VALUE,ST_DHTML_TAG_CLOSE> {S}* { + if(Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + return WHITE_SPACE; +} + +// BEGIN REGULAR XML +/* handle opening a new tag almost anywhere */ +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_DECLARATION, ST_XML_ATTRIBUTE_VALUE> {genericTagOpen} { + if(Debug.debugTokenizer) + dump("\nstart tag open");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; +} + +/* unquoted */ + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_XML_DECLARATION> {genericEndTagOpen} { + if(Debug.debugTokenizer) + dump("\nend tag open");//$NON-NLS-1$ + yybegin(ST_XML_TAG_NAME); + return XML_END_TAG_OPEN; +} +/* the tag's name was found, start scanning for attributes */ +<ST_XML_TAG_NAME> {Name} { + if(Debug.debugTokenizer) + dump("tag name");//$NON-NLS-1$ + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +/* another attribute name was found, resume looking for the equals sign */ +<ST_XML_ATTRIBUTE_NAME, ST_XML_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + yybegin(ST_XML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +/* an equal sign was found, what's next is the value */ +<ST_XML_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + yybegin(ST_XML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +/* the value was found, look for the next name */ +/* allow for unbalanced quotes, mostly a duplicate of the AttValue rule */ +<ST_XML_ATTRIBUTE_VALUE> {AttValue} | ([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} + + +/* the tag's close was found */ +<ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE> {genericTagClose} { + if(Debug.debugTokenizer) + dump("tag close");//$NON-NLS-1$ + if(isBlockMarker()) { + yybegin(ST_BLOCK_TAG_SCAN); + } + else + yybegin(YYINITIAL); + return XML_TAG_CLOSE; +} +/* the tag's close was found, but the tag doesn't need a matching end tag */ +<ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE> {genericEmptyTagClose} { + yybegin(YYINITIAL); + if(Debug.debugTokenizer) + dump("empty tag close");//$NON-NLS-1$ + return XML_EMPTY_TAG_CLOSE; +} + +<ST_XML_TAG_NAME> [^</>\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3\u4E00-\u9FA5\u3007\u3021-\u3029]* { + if(Debug.debugTokenizer) + dump("inappropriate tag name");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_CONTENT; +} + +// END REGULAR XML + + +// XML Comments + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE, ST_XML_DECLARATION> {CommentStart} { + if(Debug.debugTokenizer) + dump("\ncomment start");//$NON-NLS-1$ + yybegin(ST_XML_COMMENT); + return XML_COMMENT_OPEN; +} +<ST_XML_COMMENT> .|\r|\n { + if(Debug.debugTokenizer) + dump("comment content");//$NON-NLS-1$ + return scanXMLCommentText(); +} + +<ST_XML_COMMENT_END> {CommentEnd} { + if(Debug.debugTokenizer) + dump("comment end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_COMMENT_CLOSE; +} + +// XML misc + +{CDStart} { + if(Debug.debugTokenizer) + dump("\nCDATA start");//$NON-NLS-1$ + fStateStack.push(yystate()); + yybegin(ST_CDATA_TEXT); + return XML_CDATA_OPEN; +} +<ST_CDATA_TEXT> .|\r|\n { + if(Debug.debugTokenizer) + dump("CDATA text");//$NON-NLS-1$ + String blockContext = doBlockScan("]]>", XML_CDATA_TEXT, ST_CDATA_END);//$NON-NLS-1$ + if(blockContext == XML_CDATA_TEXT) + yybegin(ST_CDATA_END); + return blockContext; +} +<ST_CDATA_END> {CDEnd} { + if(Debug.debugTokenizer) + dump("CDATA end");//$NON-NLS-1$ + yybegin(fStateStack.pop()); + return XML_CDATA_CLOSE; +} + +<YYINITIAL> {PEReference} { + if(Debug.debugTokenizer) + dump("\nPEReference");//$NON-NLS-1$ + return XML_PE_REFERENCE; +} +<YYINITIAL> {CharRef} { + if(Debug.debugTokenizer) + dump("\nCharRef");//$NON-NLS-1$ + return XML_CHAR_REFERENCE; +} +<YYINITIAL> {EntityRef} { + if(Debug.debugTokenizer) + dump("\nEntityRef");//$NON-NLS-1$ + return XML_ENTITY_REFERENCE; +} + +<YYINITIAL> {PIstart} { + if(Debug.debugTokenizer) + dump("\nprocessing instruction start");//$NON-NLS-1$ + yybegin(ST_PI); + return XML_PI_OPEN; +} +// the next four are order dependent +<ST_PI> ((X|x)(M|m)(L|l)) { + if(Debug.debugTokenizer) + dump("XML processing instruction target");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +<ST_PI> ([iI][mM][pP][oO][rR][tT]{S}*) { + if(Debug.debugTokenizer) + dump("DHTML processing instruction target");//$NON-NLS-1$ + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +<ST_PI> xml-stylesheet { + if(Debug.debugTokenizer) + dump("XSL processing instruction target");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; +} +<ST_PI> {Name} { + if(Debug.debugTokenizer) + dump("processing instruction target");//$NON-NLS-1$ + yybegin(ST_PI_WS); + return XML_TAG_NAME; +} +<ST_PI_WS> {S}+ { + yybegin(ST_PI_CONTENT); + return WHITE_SPACE; +} +<ST_PI, ST_PI_WS> \?> { + if(Debug.debugTokenizer) + dump("processing instruction end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} +<ST_PI_CONTENT> . { + // block scan until close is found + return doScan("?>", false, XML_PI_CONTENT, ST_XML_PI_TAG_CLOSE, ST_XML_PI_TAG_CLOSE); +} +<ST_PI_CONTENT,ST_XML_PI_TAG_CLOSE> \?> { + // ended with nothing inside + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} +<ST_XML_PI_ATTRIBUTE_NAME, ST_XML_PI_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_XML_PI_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +<ST_XML_PI_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("XML processing instruction '='");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +/* the value was found, look for the next name */ +<ST_XML_PI_ATTRIBUTE_VALUE> {AttValue} | ([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("XML processing instruction attribute value");//$NON-NLS-1$ + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} +/* the PI's close was found */ +<ST_XML_PI_EQUALS, ST_XML_PI_ATTRIBUTE_NAME, ST_XML_PI_ATTRIBUTE_VALUE> {PIend} { + if(Debug.debugTokenizer) + dump("XML processing instruction end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} +// DHTML +<ST_DHTML_ATTRIBUTE_NAME, ST_DHTML_EQUALS> {Name} { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_DHTML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; +} +<ST_DHTML_EQUALS> {Eq} { + if(Debug.debugTokenizer) + dump("DHTML processing instruction '='");//$NON-NLS-1$ + yybegin(ST_DHTML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; +} +/* the value was found, look for the next name */ +<ST_DHTML_ATTRIBUTE_VALUE> {AttValue} | ([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("DHTML processing instruction attribute value");//$NON-NLS-1$ + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; +} +/* The DHTML PI's close was found */ +<ST_DHTML_EQUALS, ST_DHTML_ATTRIBUTE_NAME, ST_DHTML_ATTRIBUTE_VALUE> [/]*> { + if(Debug.debugTokenizer) + dump("DHTML processing instruction end");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_PI_CLOSE; +} + +// XML declarations + +<YYINITIAL, ST_XML_TAG_NAME, ST_XML_EQUALS, ST_XML_ATTRIBUTE_NAME, ST_XML_ATTRIBUTE_VALUE> {genericTagOpen}! { + fStateStack.push(yystate()); + if(Debug.debugTokenizer) + dump("\ndeclaration start");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION); + return XML_DECLARATION_OPEN; +} +<ST_XML_DECLARATION> [Ee][Ll][Ee][Mm][Ee][Nn][Tt] { + if(Debug.debugTokenizer) + dump("element");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION); + return XML_ELEMENT_DECLARATION; +} +<ST_XML_DECLARATION> [Dd][Oo][Cc][Tt][Yy][Pp][Ee] { + if(Debug.debugTokenizer) + dump("doctype");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_DECLARATION); + return XML_DOCTYPE_DECLARATION; +} +<ST_XML_DECLARATION> [Aa][Tt][Tt][Ll][Ii][Ss][Tt] { + if(Debug.debugTokenizer) + dump("attlist");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION); + return XML_ATTLIST_DECLARATION; +} + +// begin DOCTYPE handling procedure +<ST_XML_DOCTYPE_DECLARATION, ST_XML_DOCTYPE_EXTERNAL_ID, ST_XML_DOCTYPE_ID_SYSTEM, ST_XML_DOCTYPE_ID_PUBLIC, ST_XML_DECLARATION_CLOSE> \[[^\]]*\] { + return XML_DOCTYPE_INTERNAL_SUBSET; +} + +<ST_XML_DOCTYPE_DECLARATION> {Name} { + if(Debug.debugTokenizer) + dump("doctype type");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_EXTERNAL_ID); + return XML_DOCTYPE_NAME; +} +<ST_XML_DOCTYPE_EXTERNAL_ID> [Pp][Uu][Bb][Ll][Ii][Cc] { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_ID_PUBLIC); + return XML_DOCTYPE_EXTERNAL_ID_PUBLIC; +} +<ST_XML_DOCTYPE_EXTERNAL_ID> [Ss][Yy][Ss][Tt][Ee][Mm] { + if(Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_SYSTEM; +} +<ST_XML_DOCTYPE_ID_PUBLIC> {AttValue}|{PubidLiteral}|([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("doctype public reference");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_PUBREF; +} +<ST_XML_DOCTYPE_ID_SYSTEM> {AttValue}|{SystemLiteral}|([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("doctype system reference");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION_CLOSE); + return XML_DOCTYPE_EXTERNAL_ID_SYSREF; +} +// end DOCTYPE handling + +// begin ELEMENT handling procedure +<ST_XML_ELEMENT_DECLARATION> {AttValue}|{PubidLiteral}|([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("elementdecl name");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION_CONTENT); + return XML_ELEMENT_DECL_NAME; +} +<ST_XML_ELEMENT_DECLARATION_CONTENT> [^>]* { + if(Debug.debugTokenizer) + dump("elementdecl contentspec");//$NON-NLS-1$ + return XML_ELEMENT_DECL_CONTENT; +} + +<ST_XML_ELEMENT_DECLARATION_CONTENT> {genericTagClose} { + if(Debug.debugTokenizer) + dump("elementdecl close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; +} +// end ELEMENT handling + +// begin ATTLIST handling procedure +<ST_XML_ATTLIST_DECLARATION> {AttValue}|{PubidLiteral}|([\'\"]([^\'\"\040\011\012\015<>/]|\/+[^\'\"\040\011\012\015<>/] )* ) { + if(Debug.debugTokenizer) + dump("attlist name");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION_CONTENT); + return XML_ATTLIST_DECL_NAME; +} +<ST_XML_ATTLIST_DECLARATION_CONTENT> [^>]* { + if(Debug.debugTokenizer) + dump("attlist contentspec");//$NON-NLS-1$ + return XML_ATTLIST_DECL_CONTENT; +} + +<ST_XML_ATTLIST_DECLARATION_CONTENT> {genericTagClose} { + if(Debug.debugTokenizer) + dump("attlist close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; +} +// end ATTLIST handling + +<ST_XML_DECLARATION, ST_XML_DOCTYPE_DECLARATION, ST_XML_DOCTYPE_EXTERNAL_ID, ST_XML_ATTLIST_DECLARATION, ST_XML_ELEMENT_DECLARATION, ST_XML_DECLARATION_CLOSE, ST_XML_DOCTYPE_ID_PUBLIC, ST_XML_DOCTYPE_ID_SYSTEM, ST_XML_DOCTYPE_EXTERNAL_ID> {genericTagClose} { + if(Debug.debugTokenizer) + dump("declaration end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if(fStateStack.peek()!=YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; +} +// end DECLARATION handling + +<YYINITIAL> [^<&%]*|[&%]{S}+{Name}[^&%<]*|[&%]{Name}([^;&%<]*|{S}+;*) { + if(Debug.debugTokenizer) + dump("\nXML content");//$NON-NLS-1$ + return XML_CONTENT; +} + +<YYINITIAL> (%)+ { + if(Debug.debugTokenizer) + dump("non-reference %");//$NON-NLS-1$ + return XML_CONTENT; +} + +<ST_BLOCK_TAG_SCAN> .|\r|\n { + return doBlockTagScan(); + } + +. { + if (Debug.debugTokenizer) + System.out.println("!!!unexpected!!!: \"" + yytext() + "\":" + //$NON-NLS-2$//$NON-NLS-1$ + yychar + "-" + (yychar + yylength()));//$NON-NLS-1$ + return UNDEFINED; +} + +\040 { + if(Debug.debugTokenizer) + dump("SPACE");//$NON-NLS-1$ + return WHITE_SPACE; +} +\011 { + if(Debug.debugTokenizer) + dump("0x9");//$NON-NLS-1$ + return WHITE_SPACE; +} +\015 +{ + if(Debug.debugTokenizer) + dump("CARRIAGE RETURN");//$NON-NLS-1$ + return WHITE_SPACE; +} +\012 { + if(Debug.debugTokenizer) + dump("LINE FEED");//$NON-NLS-1$ + return WHITE_SPACE; +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flex.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flex.cmd new file mode 100644 index 0000000..59e811c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flex.cmd
@@ -0,0 +1,3 @@ +@echo off +cmd /c flexx.cmd +cmd /c flexj.cmd
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flex.sh b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flex.sh new file mode 100644 index 0000000..5cb6e3b --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flex.sh
@@ -0,0 +1,7 @@ +#!/bin/sh +export PATH=$PATH:/opt/IBMJava2-131/bin/:/opt/IBMJava2-13/bin/:/opt/jdk1.4 +java -Xmx470000000 -cp JFlex/lib/sed-jflex.jar;. JFlex.Main JSPTokenizer.jflex -skel skeleton.sse +java -Xmx470000000 -cp JFlex/lib/sed-jflex.jar;. JFlex.Main XMLTokenizer.jflex -skel skeleton.sse +rm -f JSPTokenizer.java~ JSPTokenizer~ XMLTokenizer.java~ XMLTokenizer~ +cp -v XMLTokenizer.java ../../../../sedmodel/com/ibm/sed/parser/internal +cp -v JSPTokenizer.java ../../../../sedmodel/com/ibm/sed/parser/internal
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flexj.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flexj.cmd new file mode 100644 index 0000000..e010e35 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flexj.cmd
@@ -0,0 +1,3 @@ +@echo off +PATH=%PATH%;d:\jdk6_01\bin +java -Xmx470000000 -cp d:\JFlex\1.2.2\lib\JFlex.jar;. JFlex.Main JSPTokenizer.jflex -skel skeleton.sse && rm -f JSPTokenizer.java~ JSPTokenizer~ && copy JSPTokenizer.java ..\..\..\..\..\org.eclipse.jst.jsp.core\src\org\eclipse\jst\jsp\core\internal\parser\\internal\JSPTokenizer.java
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flexx.cmd b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flexx.cmd new file mode 100644 index 0000000..fcb1f81 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/flexx.cmd
@@ -0,0 +1,3 @@ +@echo off +PATH=%PATH%;c:\jdk1.4.2_08\bin +java -Xmx470000000 -cp d:\JFlex\1.2.2\lib\JFlex.jar;. JFlex.Main XMLTokenizer.jflex -skel skeleton.sse && rm -f XMLTokenizer.java~ XMLTokenizer~ && copy XMLTokenizer.java ..\..\..\..\..\org.eclipse.wst.xml.core\src\org\eclipse\wst\xml\core\internal\parser\XMLTokenizer.java
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/skeleton.sse b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/skeleton.sse new file mode 100644 index 0000000..3e1d3c4 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/skeleton.sse
@@ -0,0 +1,351 @@ + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ +--- lexical states, charmap + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + // final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = { + "Unkown internal scanner error", //$NON-NLS-1$ + "Internal error: unknown state", //$NON-NLS-1$ + "Error: could not match input", //$NON-NLS-1$ + "Error: pushback value was too large" //$NON-NLS-1$ + }; + +--- isFinal list + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** this buffer contains the current text to be matched and is + the source of the yytext() string */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** endRead marks the last character in the buffer, that has been read + from input */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + // private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + // private int yycolumn; + + /** + * yy_atBOL == true <=> the scanner is currently at the beginning of a line + */ + // private boolean yy_atBOL; + + /** yy_atEOF == true <=> the scanner has returned a value for EOF */ + private boolean yy_atEOF; + +--- user class code + + /** + * Creates a new scanner + * There is also a java.io.InputStream version of this constructor. + * + * @param in the java.io.Reader to read input from. + */ +--- constructor declaration + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the + * end of the stream is reached. + * @exception IOException if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, + yy_buffer, 0, + yy_endRead-yy_startRead); + + /* translate stored positions */ + yy_endRead-= yy_startRead; + yy_currentPos-= yy_startRead; + yy_markedPos-= yy_startRead; + yy_pushbackPos-= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos*2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, + yy_buffer.length-yy_endRead); + + if ( numRead == -1 ) return YYEOF; + + yy_endRead+= numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String( yy_buffer, yy_startRead, yy_markedPos-yy_startRead ); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos-yy_startRead; + } + + + /** + * Reports an error that occured while scanning - from the SED JFlex skeleton + * + * @param errorCode the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + Logger.log(Logger.ERROR, YY_ERROR_MSG[errorCode]); + } + catch (ArrayIndexOutOfBoundsException e) { + Logger.log(Logger.ERROR, YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + // DO NOT EXIT the VM on an error + // System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number the number of characters to be read again. + * This number must not be greater than yylength()! + */ + void yypushback(int number) { + if ( number > yylength() ) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + /** + * user method - skeleton.sed + */ + protected final boolean containsTagName(char[] markerTagName, int offset, int tagnameLength) { + for(int j = 0; j < fBlockMarkers.size(); j++) { + BlockMarker marker = (BlockMarker)fBlockMarkers.get(j); + if(marker.getTagName().length() == tagnameLength) { + boolean matchesSoFar = true; + for(int i = 0; i < tagnameLength && matchesSoFar; i++) { + if(marker.isCaseSensitive()) { + if(marker.getTagName().charAt(i) != markerTagName[i + offset]) + matchesSoFar = false; + } + else { + if(Character.toLowerCase(marker.getTagName().charAt(i)) != Character.toLowerCase(markerTagName[i + offset])) + matchesSoFar = false; + } + } + if(matchesSoFar) + return true; + } + } + return false; + } + + /** + * user method - skeleton.sed + * + * Return ALL of the regions scannable within the remaining text + * Note: for verification use + */ + public final List getRegions() { + List tokens = new ArrayList(); + ITextRegion region = null; + try { + region = getNextToken(); + while(region != null) { + if (region != null) { + tokens.add(region); + } + region = getNextToken(); + } + } + catch (StackOverflowError e) { + Logger.logException(getClass().getName()+": input could not be tokenized correctly at position " + getOffset(), e);//$NON-NLS-1$ + throw e; + } + catch (Exception e) { + // Since this is convenience method and NOT the recommended + // way of getting tokens, many errors are simply hidden + Logger.logException("Exception not handled retrieving regions: " + e.getLocalizedMessage(), e);//$NON-NLS-1$ + } + return tokens; + } + /** + * user method - skeleton.sed + */ + private final void dump(String s) { + if (Debug.debugTokenizer) { + System.out.println(s + " (" + yychar + "-" + //$NON-NLS-2$//$NON-NLS-1$ + (yylength() + yychar) + "):\'" +//$NON-NLS-1$ + StringUtils.escape(yytext()) + "\'");//$NON-NLS-1$ + } + } + /* user method - skeleton.sed */ + public final boolean isEOF() { + return yy_atEOF; + } +/* user method - skeleton.sed */ +protected final boolean containsTagName(String markerTagName) { + Iterator blocks = fBlockMarkers.iterator(); + while(blocks.hasNext()) { + BlockMarker marker = (BlockMarker)blocks.next(); + if(marker.isCaseSensitive()) { + if(marker.getTagName().equals(markerTagName)) + return true; + } + else { + if(marker.getTagName().equalsIgnoreCase(markerTagName)) + return true; + } + } + return false; +} + +--- yy_doEof + /** + * Resumes scanning until the next regular expression is matched, + * the end of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException if any I/O-Error occurs + */ +--- yylex declaration + int yy_input; + int yy_action; + +--- local declarations + + while (true) { + +--- start admin (line, char, col count) + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + +--- start admin (lexstate etc) + + yy_forAction: { + while (true) { + + yy_input = yy_advance(); + + if ( yy_input == YYEOF ) break yy_forAction; + +--- line, col, char count, next transition, isFinal action + yy_action = yy_state; + yy_markedPos = yy_currentPos; +--- line count update + } + + } + } + +--- char count update + + switch (yy_action) { + +--- actions + default: + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; +--- eofvalue + } + else { +--- no match + } + } + } + } + +--- main + +}
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/skeleton.sse.readme b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/skeleton.sse.readme new file mode 100644 index 0000000..eb89700 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/skeleton.sse.readme
@@ -0,0 +1,6 @@ +The skeleton.sed file contains the modified JFlex 1.2.2 skeleton file with +changes for use with the tokenizers within the org.eclipse.wst.sse.core.xml and +org.eclipse.wst.sse.core.jsp plugins. + +The skeleton file's method definitions are copied into the generated output +directly.
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/build.xml b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/build.xml new file mode 100644 index 0000000..bfd83de --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/build.xml
@@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="tokenizer" default="CSSTokenizer"> + <target name="init" depends="properties"> + <property name="workspace.dir" value="../.."/> + <property name="jflex.jar" value="/DevTimeSupport/JFlex/lib/sed-jflex.jar"/> + <property name="jflex.maxmemory" value="470000000"/> + <property name="jflex.opt" value="-skel SedModel/HTMLTokenizer/devel/skeleton.sse"/> + </target> + + <target name="properties" if="eclipse.running"> + <property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/> + </target> + + <target name="CSSTokenizer" depends="init"> + <property name="src.dir" value="SedModel/CSSTokenizer/devel"/> + <property name="src.file" value="CSSTokenizer.jflex"/> + <property name="dest.dir" value="org.eclipse.wst.sse.core.css/src/com/ibm/sse/model/css/internal/parser"/> + <property name="finaldest.dir" value="org.eclipse.wst.css.core/src/org/eclipse/wst/css/core/internal/parser"/> + <antcall target="build-tokenizer"> + <param name="jflex.opt" value=""/> + </antcall> + </target> + + <target name="XMLTokenizer" depends="init"> + <property name="src.dir" value="SedModel/HTMLTokenizer/devel"/> + <property name="src.file" value="XMLTokenizer"/> + <property name="dest.dir" value="org.eclipse.wst.sse.core.xml/src/com/ibm/sse/model/xml/internal/parser"/> + <antcall target="build-tokenizer"/> + </target> + + <target name="JSPTokenizer" depends="init"> + <property name="src.dir" value="SedModel/HTMLTokenizer/devel"/> + <property name="src.file" value="JSPTokenizer.jflex"/> + <property name="dest.dir" value="org.eclipse.wst.sse.core.jsp/src/com/ibm/sse/model/jsp/parser/internal"/> + <antcall target="build-tokenizer"/> + </target> + + <target name="build-tokenizer" depends="init"> + <antcall target="run-jflex"/> + <antcall target="copy"/> + <antcall target="clean"/> + </target> + + <target name="run-jflex" depends="init"> + <java classname="JFlex.Main" maxmemory="${jflex.maxmemory}" fork="yes" > + <arg line="${src.dir}/${src.file} ${jflex.opt}"/> + <classpath> + <pathelement location="${jflex.jar}"/> + </classpath> + </java> + </target> + + <target name="copy" depends="init"> + <copy todir="${workspace.dir}/${dest.dir}" overwrite="true" verbose="true"> + <fileset dir="${src.dir}"> + <include name="*.java"/> + </fileset> + </copy> + <copy todir="${workspace.dir}/${finaldest.dir}" overwrite="true" verbose="true"> + <fileset dir="${src.dir}"> + <include name="*.java"/> + </fileset> + </copy> + </target> + + <target name="clean" depends="init"> + <delete verbose="true"> + <fileset dir="${src.dir}" defaultexcludes="no"> + <include name="*~"/> + </fileset> + </delete> + </target> + + +</project> \ No newline at end of file
diff --git a/bundles/org.eclipse.wst.sse.core/DevTimeSupport/devTimeSupportInfo.txt b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/devTimeSupportInfo.txt new file mode 100644 index 0000000..d8c776e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/DevTimeSupport/devTimeSupportInfo.txt
@@ -0,0 +1,15 @@ + + +This directory, DevTimeSupport, normally contains +the source for some of our parser generators. + +See :pserver:dev.eclipse.org:/cvsroot/webtools/sourceediting/plugins/org.eclipse.wst.sse.core/DevTimeSupport + +This particular text file you are reading +is the only one shipped +with an SDK build, since the other files +are seldom needed during development or debugging, +and some of those files cause problems with long +filename or paths that end up being too long for +some platforms, and some unzip programs. +
diff --git a/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF new file mode 100644 index 0000000..bee3db0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
@@ -0,0 +1,54 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.wst.sse.core; singleton:=true +Bundle-Version: 1.1.602.qualifier +Bundle-Activator: org.eclipse.wst.sse.core.internal.SSECorePlugin +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Export-Package: org.eclipse.wst.sse.core, + org.eclipse.wst.sse.core.indexing, + org.eclipse.wst.sse.core.internal;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.cleanup;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.document;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.encoding;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.encoding.util;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.exceptions;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.filebuffers;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.format;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.ltk.modelhandler;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.ltk.parser;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.model;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.modelhandler;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.parser;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.preferences;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional.document;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional.events;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional.exceptions;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional.model;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional.tasks;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.provisional.text;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.tasks;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.text;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.text.rules;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.undo;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.util;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.internal.validate;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", + org.eclipse.wst.sse.core.text, + org.eclipse.wst.sse.core.utils, + org.eclipse.wst.sse.internal.contentproperties;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui" +Import-Package: com.ibm.icu.util; version="3.8", + com.ibm.icu.text; version="3.8" +Require-Bundle: org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)", + org.eclipse.text;bundle-version="[3.5.0,4.0.0)";visibility:=reexport, + org.eclipse.core.jobs;bundle-version="[3.5.0,4.0.0)", + org.eclipse.core.runtime;bundle-version="[3.5.0,4.0.0)", + org.eclipse.core.filebuffers;bundle-version="[3.5.0,4.0.0)", + org.eclipse.wst.common.uriresolver;bundle-version="[1.1.301,1.2.0)", + org.eclipse.emf.common;bundle-version="[2.4.0,3.0.0)", + org.eclipse.wst.validation;bundle-version="[1.2.100,2.0.0)";resolution:=optional, + org.eclipse.core.expressions;bundle-version="[3.4.100,4.0.0)", + org.eclipse.osgi.services;bundle-version="3.2.0" +Bundle-ActivationPolicy: lazy;exclude:="org.eclipse.wst.sse.core.internal.propertytester" +Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.wst.sse.core/README.txt b/bundles/org.eclipse.wst.sse.core/README.txt new file mode 100644 index 0000000..4eb3ccf --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/README.txt
@@ -0,0 +1,2 @@ +The core model of the sse framework. It provides the base document classes, +builders and parsers. \ No newline at end of file
diff --git a/bundles/org.eclipse.wst.sse.core/about.html b/bundles/org.eclipse.wst.sse.core/about.html new file mode 100644 index 0000000..2199df3 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/about.html
@@ -0,0 +1,34 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> +<HTML> + +<head> +<title>About</title> +<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1"> +</head> + +<BODY lang="EN-US"> + +<H3>About This Content</H3> + +<P>June, 2008</P> + +<H3>License</H3> + +<P>The Eclipse Foundation makes available all content in this plug-in +("Content"). Unless otherwise indicated below, the Content is provided to you +under the terms and conditions of the Eclipse Public License Version 1.0 +("EPL"). A copy of the EPL is available at +<A href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/org/documents/epl-v10.php</A>. +For purposes of the EPL, "Program" will mean the Content.</P> + +<P>If you did not receive this Content directly from the Eclipse Foundation, the +Content is being redistributed by another party ("Redistributor") and different +terms and conditions may apply to your use of any object code in the Content. +Check the RedistributorÂ’s license that was provided with the Content. If no such +license exists, contact the Redistributor. Unless otherwise indicated below, the +terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at +<A href="http://www.eclipse.org/">http://www.eclipse.org/</A>.</P> + +</BODY> +</HTML>
diff --git a/bundles/org.eclipse.wst.sse.core/build.properties b/bundles/org.eclipse.wst.sse.core/build.properties new file mode 100644 index 0000000..37541b2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/build.properties
@@ -0,0 +1,34 @@ +############################################################################### +# Copyright (c) 2001, 2006 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 +# Jens Lukowski/Innoopract - initial renaming/restructuring +# +############################################################################### +source.. = src-encoding/,\ + src/,\ + src-tasktags/,\ + src-contentproperties/ +bin.includes = plugin.xml,\ + plugin.properties,\ + .options,\ + config/,\ + META-INF/,\ + .,\ + about.html +bin.excludes = bin/**,\ + @dot/**,\ + temp.folder/** +src.includes = schema/,\ + doc/,\ + component.xml,\ + DevTimeSupport/ +output.. = bin/ +src.excludes = DevTimeSupport/build.xml,\ + DevTimeSupport/SedModel/,\ + DevTimeSupport/HeadParsers/
diff --git a/bundles/org.eclipse.wst.sse.core/component.xml b/bundles/org.eclipse.wst.sse.core/component.xml new file mode 100644 index 0000000..dae223c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/component.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component xmlns="http://eclipse.org/wtp/releng/tools/component-model" name="org.eclipse.wst.sse"> + <description url="http://eclipse.org/webtools/wst/components/sse/overview.html"></description> + <component-depends unrestricted="true"></component-depends> + <plugin id="org.eclipse.wst.sse.core" fragment="false" /> + <plugin id="org.eclipse.wst.sse.doc.user" fragment="false" /> + <plugin id="org.eclipse.wst.sse.ui" fragment="false" /> + <package name="org.eclipse.wst.sse.ui.views.properties" api="false"> + <type name="PropertySheetConfiguration" subclass="true" instantiate="true" /> + <type name="IPropertySourceExtension" implement="true" /> + </package> + <package name="org.eclipse.wst.sse.ui" api="false"> + <type name="StructuredTextEditor" subclass="false" instantiate="true" /> + <type name="StructuredTextViewerConfiguration" subclass="true" instantiate="true" /> + </package> + <package name="org.eclipse.wst.sse.ui.views.contentoutline" api="false"> + <type name="ContentOutlineConfiguration" subclass="true" instantiate="true" /> + </package> + <plugin id="org.eclipse.wst.sse.ui.infopop" fragment="false" /> +</component> \ No newline at end of file
diff --git a/bundles/org.eclipse.wst.sse.core/config/charset.properties b/bundles/org.eclipse.wst.sse.core/config/charset.properties new file mode 100644 index 0000000..881c97e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/config/charset.properties
@@ -0,0 +1,100 @@ +############################################################################### +# Copyright (c) 2001, 2011 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 +############################################################################### +! the number of codeset names +totalnumber=26 + +! Only translate the xx.label strings. Do not translate the xx.iana, xx.prefName or xx.java strings. + +codeset.0.label=ISO 10646/Unicode(UTF-8) +codeset.0.iana=UTF-8 + +codeset.1.label=ISO 10646/Unicode(UTF-16) Big Endian +codeset.1.iana=UTF-16 + +codeset.2.label=ISO 10646/Unicode(UTF-16BE) Big Endian +codeset.2.iana=UTF-16BE + +codeset.3.label=ISO 10646/Unicode(UTF-16LE) Little Endian +codeset.3.iana=UTF-16LE + +codeset.4.label=US ASCII +codeset.4.iana=US-ASCII + +codeset.5.label=ISO Latin-1 +codeset.5.iana=ISO-8859-1 + +codeset.6.label=ISO Latin-9 +codeset.6.iana=ISO-8859-15 + +! (Slavic: Czech, Croat, German, Hungarian, Polish, Romanian, Slovak, Slovenian) +codeset.7.label=Central/East European (Slavic) +codeset.7.iana=ISO-8859-2 + +! (Esperanto, Galician, Maltese, Turkish) +codeset.8.label=Southern European +codeset.8.iana=ISO-8859-3 + +codeset.9.label=Arabic, Logical +codeset.9.iana=ISO-8859-6 + +codeset.10.label=Arabic +codeset.10.iana=windows-1256 + +codeset.11.label=Chinese, National Standard +codeset.11.iana=GB18030 + +codeset.12.label=Traditional Chinese, Big5 +codeset.12.iana=BIG5 + +! (Estonian, Latvian, Lithuanian) +codeset.13.label=Cyrillic, ISO-8859-4 +codeset.13.iana=ISO-8859-4 + +! (Bulgarian, Byelorussian, Macedonian, Serbian, Ukrainian) +codeset.14.label=Cyrillic, ISO-8859-5 +codeset.14.iana=ISO-8859-5 + +codeset.15.label=Greek +codeset.15.iana=ISO-8859-7 + +codeset.16.label=Hebrew, Visual +codeset.16.iana=ISO-8859-8 + +! label=Hebrew, Logical +! iana=ISO-8859-8-I + +codeset.17.label=Hebrew +codeset.17.iana=windows-1255 + +codeset.18.label=Japanese, EUC-JP +codeset.18.iana=EUC-JP + +codeset.19.label=Japanese, ISO 2022 +codeset.19.iana=ISO-2022-JP + +codeset.20.label=Japanese, Shift-JIS +codeset.20.iana=Shift_JIS + +codeset.21.label=Japanese, Windows-31J +codeset.21.iana=Windows-31J +codeset.21.prefName=windows-31j + +codeset.22.label=Korean, EUC-KR +codeset.22.iana=EUC-KR + +codeset.23.label=Korean, ISO 2022 +codeset.23.iana=ISO-2022-KR + +codeset.24.label=Thai, TISI +codeset.24.iana=TIS-620 + +codeset.25.label=Turkish +codeset.25.iana=ISO-8859-9
diff --git a/bundles/org.eclipse.wst.sse.core/config/defaultIANA.properties b/bundles/org.eclipse.wst.sse.core/config/defaultIANA.properties new file mode 100644 index 0000000..f47ed6e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/config/defaultIANA.properties
@@ -0,0 +1,26 @@ +############################################################################### +# Copyright (c) 2001, 2006 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 +############################################################################### +# This file lists charset mappings between an encoding and its default +# equivalent encoding listed in charset.properties +# format: anyname=iana name listed in charset.properties + +# No translation necessary +windows-1252=ISO-8859-1 +windows-932=Shift_JIS +x-euc-jp-linux=EUC-JP +windows-949=EUC-KR +x-windows-949=EUC-KR +windows-950=BIG5 +x-windows-950=BIG5 +windows-936=GB18030 +x-mswin-936=GB18030 +GB2312=GB18030 +x-EUC-CN=GB18030
diff --git a/bundles/org.eclipse.wst.sse.core/config/override.properties b/bundles/org.eclipse.wst.sse.core/config/override.properties new file mode 100644 index 0000000..ae703ba --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/config/override.properties
@@ -0,0 +1,24 @@ +############################################################################### +# Copyright (c) 2001, 2005 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 +# Jens Lukowski/Innoopract - initial renaming/restructuring +# +############################################################################### +# This file lists charset mappings which supplement or override those +# provided by the VM. There are few cases where this should be required. +# Note: there is no integrety checking to see if the validCharsetName is indeed +# valid for a given VM, so errors would only show up when attempted to +# be used. + +# No translation necessary + +# format: anyname=validjavaCharsetName +ISO-8859-8-I=ISO-8859-8 +X-SJIS=Shift_JIS +X-EUC-JP=Shift_JIS
diff --git a/bundles/org.eclipse.wst.sse.core/doc/book.css b/bundles/org.eclipse.wst.sse.core/doc/book.css new file mode 100644 index 0000000..9278cd8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/doc/book.css
@@ -0,0 +1,106 @@ +P.Code { + display: block; + text-align: left; + text-indent: 0.00pt; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 15pt; + font-size: 10.000000pt; + font-weight: medium; + font-style: Regular; + color: #4444CC; + text-decoration: none; + vertical-align: baseline; + text-transform: none; + font-family: "Courier New"; +} +H6.CaptionFigColumn { + display: block; + text-align: left; + text-indent: 0.000000pt; + margin-top: 3.000000pt; + margin-bottom: 11.000000pt; + margin-right: 0.000000pt; + margin-left: 0.000000pt; + font-size: 9.000000pt; + font-weight: medium; + font-style: Italic; + color: #000000; + text-decoration: none; + vertical-align: baseline; + text-transform: none; + font-family: "Arial"; +} +P.Note { + display: block; + text-align: left; + text-indent: 0pt; + margin-top: 19.500000pt; + margin-bottom: 19.500000pt; + margin-right: 0.000000pt; + margin-left: 30pt; + font-size: 11.000000pt; + font-weight: medium; + font-style: Italic; + color: #000000; + text-decoration: none; + vertical-align: baseline; + text-transform: none; + font-family: "Arial"; +} +EM.UILabel { + font-weight: Bold; + font-style: Regular; + text-decoration: none; + vertical-align: baseline; + text-transform: none; +} +EM.CodeName { + font-weight: Bold; + font-style: Regular; + text-decoration: none; + vertical-align: baseline; + text-transform: none; + font-family:"Courier New"; +} + + + + +/* following font face declarations need to be removed for DBCS */ + +body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-family: Arial, Helvetica, sans-serif; color: #000000} +pre { font-family: Courier, monospace} + +/* end font face declarations */ + +/* following font size declarations should be OK for DBCS */ +body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-size: 10pt; } +pre { font-size: 10pt} + +/* end font size declarations */ + +body { background: #FFFFFF} +h1 { font-size: 18pt; margin-top: 5; margin-bottom: 1 } +h2 { font-size: 14pt; margin-top: 25; margin-bottom: 3 } +h3 { font-size: 11pt; margin-top: 20; margin-bottom: 3 } +h4 { font-size: 10pt; margin-top: 20; margin-bottom: 3; font-style: italic } +p { margin-top: 10px; margin-bottom: 10px } +pre { margin-left: 6; font-size: 9pt } +a:link { color: #0000FF } +a:hover { color: #000080 } +a:visited { text-decoration: underline } +ul { margin-top: 0; margin-bottom: 10 } +li { margin-top: 0; margin-bottom: 0 } +li p { margin-top: 0; margin-bottom: 0 } +ol { margin-top: 0; margin-bottom: 10 } +dl { margin-top: 0; margin-bottom: 10 } +dt { margin-top: 0; margin-bottom: 0; font-weight: bold } +dd { margin-top: 0; margin-bottom: 0 } +strong { font-weight: bold} +em { font-style: italic} +var { font-style: italic} +div.revision { border-left-style: solid; border-left-width: thin; + border-left-color: #7B68EE; padding-left:5 } +th { font-weight: bold }
diff --git a/bundles/org.eclipse.wst.sse.core/doc/schema.css b/bundles/org.eclipse.wst.sse.core/doc/schema.css new file mode 100644 index 0000000..4efbf26 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/doc/schema.css
@@ -0,0 +1,66 @@ +H6.CaptionFigColumn#header { + font-size:16px; + display:inline +} + +P.Note#copyright { + font-size: smaller; + font-style: normal; + color: #336699; + display:inline; + margin-top: 3.000000pt; + margin-bottom: 11.000000pt; + margin-right: 0.000000pt; + margin-left: 0.000000pt; +} + +P.Code#dtd { + color: #800000; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 10.000000pt; +} + +P.Code#dtdAttlist { + color: #800000; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 20.000000pt; +} + +P.Code#tag { + color: #000080; + display:inline; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 0.000000pt; +} + +P.Code#cstring { + color: #008000; + display:inline; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 0.000000pt; +} + +.ConfigMarkup#elementDesc { + color: black; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 10.000000pt; +} + +.ConfigMarkup#attlistDesc { + color: black; + margin-top: 0.000000pt; + margin-bottom: 0.000000pt; + margin-right: 0.000000pt; + margin-left: 32.000000pt; +} +
diff --git a/bundles/org.eclipse.wst.sse.core/handyStuff.jpage b/bundles/org.eclipse.wst.sse.core/handyStuff.jpage new file mode 100644 index 0000000..6447b4e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/handyStuff.jpage Binary files differ
diff --git a/bundles/org.eclipse.wst.sse.core/plugin.properties b/bundles/org.eclipse.wst.sse.core/plugin.properties new file mode 100644 index 0000000..1e23274 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/plugin.properties
@@ -0,0 +1,25 @@ +############################################################################### +# Copyright (c) 2001, 2010 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 +# Jens Lukowski/Innoopract - initial renaming/restructuring +# +############################################################################### +providerName=Eclipse Web Tools Platform +pluginName=Structured Source Model +# extension names +Adapt_On_Create_Factory_Extension.name=Adapt On Create Factory Extension +Document_Types_Extension.name=Document Types Extension +JSP_Embedded_Content_Type_Handler_Extension.name=JSP Embedded Content Type Handler Extension +Content_Type_Factory_Contribution_Extension.name=Content Type Factory Contribution Extension +CSS_Profile_Extension.name=CSS Profile Extension +Comment_Element_Handler_Extension.name=Comment Element Handler Extension +Model_Handler_Extension.name=Model Handler Extension +Format_Processors_Extension_Point.name=Format Processors Extension Point +Task_Scanner_Extension_Point.name=Task Scanner Extension Point +Structured_Sources_Task_Extension.name=Detected Task
diff --git a/bundles/org.eclipse.wst.sse.core/plugin.xml b/bundles/org.eclipse.wst.sse.core/plugin.xml new file mode 100644 index 0000000..1b9d769 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/plugin.xml
@@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.0"?> +<plugin> + + + <!-- deprecated --> + <extension-point id="adaptOnCreateFactory" name="%Adapt_On_Create_Factory_Extension.name" schema="schema/adaptOnCreateFactory.exsd"/> + <!-- not for use by clients (internal)--> + <extension-point id="documentTypes" name="%Document_Types_Extension.name" schema="schema/documentTypes.exsd"/> + <!-- not for use by clients (internal)--> + <extension-point id="embeddedTypeHandler" name="%JSP_Embedded_Content_Type_Handler_Extension.name" schema="schema/embeddedTypeHandler.exsd"/> + <!-- deprecated --> + <extension-point id="contentTypeFactoryContribution" name="%Content_Type_Factory_Contribution_Extension.name" schema="schema/contentTypeFactoryContribution.exsd"/> + <!-- I commented out ... clients should just use the normal platform way of registering these + adapterFactories. At worst, they'd need a small plugin with 'startup' specified ... but that's + better than us getting hit with the performance impact (of loading plugins and pre-reqs, etc.). + <extension-point id="uriResolverAdapterFactory" name="Resource Adapter Factory for Creating URIResolvers"/> + --> + <!-- not for use by clients (internal)--> + <extension-point id="cssprofile" name="%CSS_Profile_Extension.name" schema="schema/cssprofile.exsd"/> + <!-- not for use by clients (internal)--> + <extension-point id="commentElementHandler" name="%Comment_Element_Handler_Extension.name" schema="schema/commentElementHandler.exsd"/> + + <!-- not for use by clients (internal)--> + <extension-point id="modelHandler" name="%Model_Handler_Extension.name" schema="schema/modelHandler.exsd"/> + <!-- not for use by clients (internal)--> + <extension-point + id="formatProcessors" + name="%Format_Processors_Extension_Point.name" + schema="schema/formatProcessors.exsd" /> + + <!-- not for use by clients (internal)--> + <extension-point id="taskscanner" name="%Task_Scanner_Extension_Point.name" schema="schema/taskscanner.exsd"/> + + <!-- Make sure default preference values are set at runtime --> + <extension point="org.eclipse.core.runtime.preferences"> + <initializer + class="org.eclipse.wst.sse.core.internal.preferences.PreferenceInitializer" /> + </extension> + + <extension point="org.eclipse.wst.validation.validatorGroup"> + <group id="structuredModelGroup"> + <listener class="org.eclipse.wst.sse.core.internal.validate.ValidatorGroupListener"/> + </group> + </extension> + + <!-- Define the StructuredFilePropertyTester as it will handle tracing the entire --> + <!-- content type hierarchy for a file. Only operates on IFiles --> + <!-- Deprecated, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=288216 --> + <extension point="org.eclipse.core.expressions.propertyTesters"> + <propertyTester + class="org.eclipse.wst.sse.core.internal.propertytester.StructuredFilePropertyTester" + id="org.eclipse.wst.sse.core.expressions.propertyTester.StructuredFilePropertyTester" + namespace="org.eclipse.wst.sse.core.resources" + properties="contentTypeId" + type="org.eclipse.core.resources.IFile"> + </propertyTester> + </extension> +</plugin>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/adaptOnCreateFactory.exsd b/bundles/org.eclipse.wst.sse.core/schema/adaptOnCreateFactory.exsd new file mode 100644 index 0000000..ddbf899 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/adaptOnCreateFactory.exsd
@@ -0,0 +1,93 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="adaptOnCreateFactory" name="Adapt On Create Factory Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is deprecated and should not be used.</b> + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2000, 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/commentElementHandler.exsd b/bundles/org.eclipse.wst.sse.core/schema/commentElementHandler.exsd new file mode 100644 index 0000000..633c80c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/commentElementHandler.exsd
@@ -0,0 +1,112 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appinfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="commentElementHandler" name="Comment Element Handler Extension Point"/> + </appinfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appinfo> + <meta.element /> + </appinfo> + </annotation> + <complexType> + <sequence> + <element ref="handler-custom"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appinfo> + <meta.attribute translatable="true"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="handler-custom"> + <complexType> + <sequence> + <element ref="startwith"/> + </sequence> + <attribute name="commenttype" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appinfo> + <meta.attribute kind="java" basedOn=":org.eclipse.wst.xml.core.internal.commentelement.CommentElementHandler"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="startwith"> + <complexType> + <attribute name="prefix" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appinfo> + <meta.section type="since"/> + </appinfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + + + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + Copyright (c) 2005, 2008 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/contentTypeFactoryContribution.exsd b/bundles/org.eclipse.wst.sse.core/schema/contentTypeFactoryContribution.exsd new file mode 100644 index 0000000..cf8418e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/contentTypeFactoryContribution.exsd
@@ -0,0 +1,93 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="contentTypeFactoryContribution" name="Content Type Factory Contribution Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is deprecated and should not be used.</b> + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/cssprofile.exsd b/bundles/org.eclipse.wst.sse.core/schema/cssprofile.exsd new file mode 100644 index 0000000..923e853 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/cssprofile.exsd
@@ -0,0 +1,130 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="cssprofile" name="CSS Profile Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="profile" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="profile"> + <complexType> + <attribute name="name" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + <attribute name="default" type="boolean"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="uri" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/documentTypes.exsd b/bundles/org.eclipse.wst.sse.core/schema/documentTypes.exsd new file mode 100644 index 0000000..b90f35f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/documentTypes.exsd
@@ -0,0 +1,172 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="documentTypes" name="Document Types Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="documentType" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="documentType"> + <complexType> + <attribute name="elementName" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="displayName" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + <attribute name="namespaceURI" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="isXHTML" type="boolean"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="hasFrameset" type="boolean"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="publicID" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="systemID" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="defaultXHTML" type="boolean"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="isWML" type="boolean"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="defaultWML" type="boolean"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/embeddedTypeHandler.exsd b/bundles/org.eclipse.wst.sse.core/schema/embeddedTypeHandler.exsd new file mode 100644 index 0000000..bbcde8d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/embeddedTypeHandler.exsd
@@ -0,0 +1,109 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="embeddedTypeHandler" name="JSP Embedded Content Type Handler Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="embeddedTypeHandler"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="embeddedTypeHandler"> + <complexType> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="apiInfo"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="implementation"/> + </appInfo> + <documentation> + + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/formatProcessors.exsd b/bundles/org.eclipse.wst.sse.core/schema/formatProcessors.exsd new file mode 100644 index 0000000..8ed7304 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/formatProcessors.exsd
@@ -0,0 +1,108 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="formatProcessors" name="Format Processors Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is deprecated and should not be used.</b> + </documentation> + </annotation> + + <element name="extension"> + <complexType> + <sequence> + <element ref="processor"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + a fully qualified identifier of the target extension point + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + an optional identifier of the extension instance + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + an optional name of the extension instance + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="processor"> + <complexType> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java"/> + </appInfo> + </annotation> + </attribute> + <attribute name="contentTypeId" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is deprecated and should not be used.</b> + </documentation> + </annotation> + + <annotation> + <appInfo> + <meta.section type="examples"/> + </appInfo> + <documentation> + <pre> + <extension + point="org.eclipse.wst.sse.core.formatProcessors"> + <processor + class="org.eclipse.wst.sse.core.xml.format.FormatProcessorXML" + contentTypeId="org.eclipse.core.runtime.xml"> + </processor> + </extension> +</pre> + </documentation> + </annotation> + + + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005, 2007 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/modelHandler.exsd b/bundles/org.eclipse.wst.sse.core/schema/modelHandler.exsd new file mode 100644 index 0000000..ea31dfa --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/modelHandler.exsd
@@ -0,0 +1,114 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="modelHandler" name="Model Handler Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element /> + </appInfo> + </annotation> + <complexType> + <sequence minOccurs="0" maxOccurs="unbounded"> + <element ref="modelHandler"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="modelHandler"> + <complexType> + <attribute name="id" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="identifier"/> + </appInfo> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn="org.eclipse.wst.sse.core.internal.ltk.modelhandler.AbstractModelHandler:"/> + </appInfo> + </annotation> + </attribute> + <attribute name="associatedContentTypeId" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="default" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + + + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005, 2009 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/schema/taskscanner.exsd b/bundles/org.eclipse.wst.sse.core/schema/taskscanner.exsd new file mode 100644 index 0000000..1f80a82 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/schema/taskscanner.exsd
@@ -0,0 +1,104 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.wst.sse.core" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.wst.sse.core" id="taskscanner" name="Task Scanner Extension Point"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element internal="true" /> + </appInfo> + </annotation> + <complexType> + <sequence> + <element ref="scanner"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="scanner"> + <complexType> + <attribute name="contentTypeIds" type="string" use="required"> + <annotation> + <documentation> + Content type identifiers for which this scanner applies, separated by commas. + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + Scanner implementation. + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn=":org.eclipse.wst.sse.core.internal.provisional.tasks.IFileTaskScanner"/> + </appInfo> + </annotation> + </attribute> + <attribute name="id" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appInfo> + <meta.section type="since"/> + </appInfo> + <documentation> + <b>This extension point is internal and should not be used by any other plugins.</b> + </documentation> + </annotation> + + + + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + </documentation> + </annotation> + +</schema>
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/AbstractContentSettingsHandler.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/AbstractContentSettingsHandler.java new file mode 100644 index 0000000..420e3a0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/AbstractContentSettingsHandler.java
@@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + +import org.eclipse.core.resources.IResourceDelta; + +/** + * @deprecated This is package protected so no one cares anyways. + */ +abstract class AbstractContentSettingsHandler implements IContentSettingsHandler { + + + private IContentSettings contentSettings = ContentSettingsCreator.create(); + private IResourceDelta fDelta; + + private void commonWithRespectToKind() { + getContentSettings().releaseCache(); + } + + protected final IContentSettings getContentSettings() { + return contentSettings; + } + + protected final IResourceDelta getDelta() { + return fDelta; + } + + + /* + * @see IContentSettingsHandler#handle(IResourceDelta) + */ + public void handle(final IResourceDelta delta) { + this.fDelta = delta; + // get Resource delta kind + final int kind = delta.getKind(); + // never used!? + //IResource resource = delta.getResource(); + + + switch (kind) { + case IResourceDelta.CHANGED : { + handleChanged(); + } + break; + case IResourceDelta.REMOVED : { + handleRemoved(); + } + break; + case IResourceDelta.ADDED : { + handleAdded(); + } + break; + + + } + + + + } + + protected void handleAdded() { + commonWithRespectToKind(); + } + + protected void handleChanged() { + commonWithRespectToKind(); + } + + protected void handleRemoved() { + commonWithRespectToKind(); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/AbstractSubject.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/AbstractSubject.java new file mode 100644 index 0000000..fcc98bc --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/AbstractSubject.java
@@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public abstract class AbstractSubject implements INotify, ISubject { + + + + private static Map listenerList = new Hashtable(); + + public synchronized void addListener(IContentSettingsListener listener) { + listenerList.put(listener, listener); + } + + /* + * @see IContentSettingsEventSubject#notifyContentSettingsListeners(Object) + */ + public synchronized void notifyListeners(org.eclipse.core.resources.IResource changedResource) { + + Set keys = listenerList.keySet(); + Iterator iter = keys.iterator(); + + while (iter.hasNext()) { + IContentSettingsListener csl = (IContentSettingsListener) iter.next(); + csl.contentSettingsChanged(changedResource); + } + } + + public synchronized void removeListener(IContentSettingsListener listener) { + listenerList.remove(listener); + } + + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettings.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettings.java new file mode 100644 index 0000000..8dca387 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettings.java
@@ -0,0 +1,690 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300443 - some constants aren't static final + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public class ContentSettings implements IContentSettings { + private static final String contentSettingsName = ".contentsettings";//$NON-NLS-1$ + + private static SimpleNodeOperator domOperator; + + private static IProject preProject; + + private static final IContentSettings singleton = new ContentSettings(); + + public static final String getContentSettingsName() { + return contentSettingsName; + } + + public synchronized static IContentSettings getInstance() { + return singleton; + } + + private String contentSettingsPath; + private IProject currProject; + private static final String fileElementName = "file";//$NON-NLS-1$ + private static final String PATHATTR = "path"; //$NON-NLS-1$ + private static final String projectElementName = "project";//$NON-NLS-1$ + + + + private static final String rootElementName = "contentsettings";//$NON-NLS-1$ + + + + private ContentSettings() { + currProject = null; + contentSettingsPath = null; + } + + + private void _setProperties(final IResource resource, final Map properties) { + if (resource == null || properties == null || properties.isEmpty()) + return; + if (!(resource.getType() == IResource.PROJECT || resource.getType() == IResource.FILE)) + return; + + contentSettingsPath = getContentSettingsPath(resource); + if (contentSettingsPath == null) + return; + + try { + + if (!existsContentSettings()) { + // create DOM tree for new XML Document + createNewDOMTree(); + } + else { + // create DOM tree from existing contentsettings. + createDOMTree(); + } + + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e) { + Logger.logException(e); + try { + createNewDOMTree(); + writeDOMDocument(); + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return; + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return; + } + + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e) { + Logger.logException(e); + preProject = currProject; + return; + } + + Element e = null; + if (resource.getType() == IResource.PROJECT) { + e = (Element) domOperator.getElementWithNodeName(projectElementName); + if (e == null) { + // create project Element and add it into tree + e = (Element) domOperator.addElementUnderRoot(projectElementName); + } + } + else if (resource.getType() == IResource.FILE) { + // check exists file Element + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(resource)); + if (e == null) { + // create file Element and add path into it. + e = (Element) domOperator.addElementUnderRoot(fileElementName); + domOperator.addAttributeAt(e, getPathAttr(), getRelativePathFromProject(resource)); + } + } + + // check exists propertyName attribute + Map attrList = domOperator.getAttributesOf(e); + boolean hasAttr = true; + if (attrList == null || attrList.isEmpty()) + hasAttr = false; + Set keys = properties.keySet(); + Iterator ii = keys.iterator(); + while (ii.hasNext()) { + String propertyName = (String) ii.next(); + String propertyValue = (String) properties.get(propertyName); + + + if (!hasAttr || (String) attrList.get(propertyName) == null) + // create propertyName attribute and add + domOperator.addAttributeAt(e, propertyName, propertyValue); + else + // set attribute value + domOperator.updateAttributeAt(e, propertyName, propertyValue); + } + + // write dom tree into .contentsettings + try { + writeDOMDocument(); + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException ex) { + Logger.logException(ex); + preProject = currProject; + return; + } + + preProject = currProject; + + + } + + + private void _setProperty(final IResource resource, final String propertyName, final String propertyValue) { + if (resource == null || propertyName == null) + return; + if (!(resource.getType() == IResource.PROJECT || resource.getType() == IResource.FILE)) + return; + + contentSettingsPath = getContentSettingsPath(resource); + if (contentSettingsPath == null) + return; + + try { + + if (!existsContentSettings()) { + // create DOM tree for new XML Document + createNewDOMTree(); + } + else { + // create DOM tree from existing contentsettings. + createDOMTree(); + } + + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e) { + Logger.logException(e); + try { + createNewDOMTree(); + writeDOMDocument(); + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return; + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return; + } + + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e) { + Logger.logException(e); + preProject = currProject; + return; + } + + Element e = null; + if (resource.getType() == IResource.PROJECT) { + e = (Element) domOperator.getElementWithNodeName(projectElementName); + if (e == null) { + // create project Element and add it into tree + e = (Element) domOperator.addElementUnderRoot(projectElementName); + } + } + else if (resource.getType() == IResource.FILE) { + // check exists file Element + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(resource)); + if (e == null) { + // create file Element and add path into it. + e = (Element) domOperator.addElementUnderRoot(fileElementName); + domOperator.addAttributeAt(e, getPathAttr(), getRelativePathFromProject(resource)); + } + } + + // check exists propertyName attribute + + Map attrList = domOperator.getAttributesOf(e); + if (attrList == null || attrList.isEmpty() || (String) attrList.get(propertyName) == null) + // create propertyName attribute and add + domOperator.addAttributeAt(e, propertyName, propertyValue); + else + // set attribute value + domOperator.updateAttributeAt(e, propertyName, propertyValue); + + + // write dom tree into .contentsettings + try { + writeDOMDocument(); + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException ex) { + Logger.logException(ex); + preProject = currProject; + return; + } + + preProject = currProject; + + + } + + + private void createDOMTree() throws SimpleNodeOperator.ReadContentSettingsFailureException { + if (domOperator == null || (currProject != null && (!currProject.equals(preProject)) && contentSettingsPath != null)) + domOperator = new SimpleNodeOperator(contentSettingsPath); + + + } + + /* + * private void createNewDOMTree() throws + * SimpleNodeOperator.CreateContentSettingsFailureException{ // create New + * document when no file exists. DOMImplementation impl = + * DOMImplementationImpl.getDOMImplementation(); Document document = + * impl.createDocument(null,rootElementName,null); domOperator = new + * SimpleNodeOperator(document); } + */ + private void createNewDOMTree() throws SimpleNodeOperator.CreateContentSettingsFailureException { + Document document = null; + try { + document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + catch (ParserConfigurationException e) { + Logger.logException("exception creating document", e); //$NON-NLS-1$ + } + catch (FactoryConfigurationError e) { + Logger.logException("exception creating document", e); //$NON-NLS-1$ + } + if (document != null) { + document.appendChild(document.createElement(rootElementName)); + domOperator = new SimpleNodeOperator(document); + } + } + + + /** + * + */ + public synchronized void deleteAllProperties(final IResource deletedFile) { + if (deletedFile == null) + return; + // if (deletedFile.exists()) return; + if ((deletedFile).getType() != IResource.FILE && (deletedFile).getType() != IResource.PROJECT) + return; + + + contentSettingsPath = getContentSettingsPath(deletedFile);// getProjectOf((IResource)deletedFile) + // + + // IPath.SEPARATOR + // + + // contentSettingsName; + if (contentSettingsPath == null) + return; + if (!existsContentSettings()) + return; + + try { + createDOMTree(); + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e) { + Logger.logException(e); + return; + } + + Element e = null; + if (deletedFile.getType() == IResource.PROJECT) + // select project element and get attribute + e = (Element) domOperator.getElementWithNodeName(projectElementName); + else if (deletedFile.getType() == IResource.FILE) + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(deletedFile)); + if (e == null) { + preProject = currProject; + return; + } + + + // when deletedFile entry exists. + if (deletedFile.getType() == IResource.PROJECT) + domOperator.removeElementWith(projectElementName); + else if (deletedFile.getType() == IResource.FILE) + domOperator.removeElementWith(getPathAttr(), getRelativePathFromProject(deletedFile)); + + + // write dom tree into .contentsettings + try { + writeDOMDocument(); + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException ex) { + Logger.logException(ex); + preProject = currProject; + return; + } + + preProject = currProject; + + + } + + public synchronized void deleteProperty(final IResource resource, final String propertyName) { + if (resource == null) + return; + if (!(resource.getType() == IResource.PROJECT || resource.getType() == IResource.FILE)) + return; + contentSettingsPath = getContentSettingsPath(resource); + if (contentSettingsPath == null) + return; + + if (!existsContentSettings()) { + return; // when .contentsettings.xml is NOT exist. + } + try { + createDOMTree(); + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e) { + Logger.logException(e); + try { + createNewDOMTree(); + writeDOMDocument(); + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return; + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return; + } + } + + Element e = null; + if (resource.getType() == IResource.PROJECT) + // select project element and get attribute + e = (Element) domOperator.getElementWithNodeName(projectElementName); + else if (resource.getType() == IResource.FILE) + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(resource)); + + + if (e != null) { + domOperator.removeAttributeAt(e, propertyName); + // write dom tree into .contentsettings + try { + writeDOMDocument(); + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException ex) { + Logger.logException(ex); + preProject = currProject; + return; + } + } + + preProject = currProject; + + } + + + private boolean existsContentSettings() { + if (contentSettingsPath == null) + return false; + + IResource file = currProject.getFile(contentSettingsName); + if (file == null) + return false; + if (file.isAccessible()) + return true; + else + return false; + + } + + public boolean existsProperties(IResource resource) { + if (resource == null) + return false; + + if (!(resource.getType() == IResource.PROJECT || resource.getType() == IResource.FILE)) + return false; + + contentSettingsPath = getContentSettingsPath(resource); + if (contentSettingsPath == null) + return false; + + if (!existsContentSettings()) + return false; // when .contentsettings.xml is NOT exist. + + try { + createDOMTree(); + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e1) { + return false; + } + + Element e = null; + if (resource.getType() == IResource.PROJECT) + // select project element and get attribute + e = (Element) domOperator.getElementWithNodeName(projectElementName); + else if (resource.getType() == IResource.FILE) + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(resource)); + + preProject = currProject; + if (e == null) + return false; + + Map properties = domOperator.getAttributesOf(e); + if (properties == null) + return false; + properties.remove(getPathAttr());// if IFile,removed + if (properties.isEmpty()) + return false; + return true; + + } + + private String getContentSettingsPath(IResource resource) { + IProject project = null; + if (resource.getType() == IResource.PROJECT) + project = (IProject) resource; + else + project = resource.getProject(); + + IPath projectLocation = project.getLocation(); + if (projectLocation == null) { + /** + * As a deprecated class, perfect operation in new scenarios such + * as with EFS is not promised. + */ + return SSECorePlugin.getDefault().getStateLocation().append(rootElementName).append(project.getName()).toString(); + } + + return projectLocation.addTrailingSeparator().append(contentSettingsName).toString(); + } + + public final String getPathAttr() { + return PATHATTR; + } + + public synchronized Map getProperties(final IResource resource) { + if (resource == null) + return null; + + if (!(resource.getType() == IResource.PROJECT || resource.getType() == IResource.FILE)) + return null; + + contentSettingsPath = getContentSettingsPath(resource); + if (contentSettingsPath == null) + return null; + + if (!existsContentSettings()) { + return null; // when .contentsettings.xml is NOT exist. + } + + + try { + createDOMTree(); + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e1) { + Logger.logException(e1); + // create DOM tree for new XML Document + try { + createNewDOMTree(); + writeDOMDocument(); + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return null; + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return null; + } + + } + + + Element e = null; + if (resource.getType() == IResource.PROJECT) + // select project element and get attribute + e = (Element) domOperator.getElementWithNodeName(projectElementName); + else if (resource.getType() == IResource.FILE) + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(resource)); + + + if (e != null) { + + Map properties = domOperator.getAttributesOf(e); + preProject = currProject; + if (properties == null) + return null; + if (properties.isEmpty()) + return null; + properties.remove(getPathAttr()); + return properties; + } + else { + preProject = currProject; + return null;// when project or file element is NOT exist. + } + } + + public synchronized String getProperty(final IResource resource, final String propertyName) { + if (resource == null) + return null; + + if (!(resource.getType() == IResource.PROJECT || resource.getType() == IResource.FILE)) + return null; + + contentSettingsPath = getContentSettingsPath(resource); + if (contentSettingsPath == null) + return null; + + if (!existsContentSettings()) { + return null; // when .contentsettings.xml is NOT exist. + } + + + try { + createDOMTree(); + } + catch (SimpleNodeOperator.ReadContentSettingsFailureException e1) { + Logger.logException(e1); + // create DOM tree for new XML Document + try { + createNewDOMTree(); + writeDOMDocument(); + } + catch (SimpleNodeOperator.CreateContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return null; + } + catch (SimpleNodeOperator.WriteContentSettingsFailureException e2) { + Logger.logException(e2); + preProject = currProject; + return null; + } + + } + + + Element e = null; + if (resource.getType() == IResource.PROJECT) + // select project element and get attribute + e = (Element) domOperator.getElementWithNodeName(projectElementName); + else if (resource.getType() == IResource.FILE) + e = (Element) domOperator.getElementWithAttribute(getPathAttr(), getRelativePathFromProject(resource)); + + + if (e != null) { + String result = e.getAttribute(propertyName); + preProject = currProject; + return result; + } + else { + preProject = currProject; + return null;// when project or file element is NOT exist. + } + } + + private String getRelativePathFromProject(IResource resource) { + if (resource == null) + return null; + + IPath path = resource.getProjectRelativePath(); + if (path == null) + return null; // if resource is project or workspace root + String resourcePath = path.toString(); + + return resourcePath; + + } + + public synchronized void releaseCache() { + domOperator = null; + } + + public synchronized void setProperties(final IResource resource, final Map properties) { + // deny to set "path" attribute value. + Set keys = properties.keySet(); + Iterator ii = keys.iterator(); + while (ii.hasNext()) { + if (this.getPathAttr().equals(ii.next())) + return; + } + this._setProperties(resource, properties); + } + + public synchronized void setProperty(final IResource resource, final String propertyName, final String propertyValue) { + // deny to set "path" attribute value. + if (this.getPathAttr().equals(propertyName)) + return; + this._setProperty(resource, propertyName, propertyValue); + } + + private void writeDOMDocument() throws SimpleNodeOperator.WriteContentSettingsFailureException { + try { + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + domOperator.writeDocument(outputStream); + outputStream.flush(); + outputStream.close(); + + ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + + IFile outputFile = currProject.getFile(contentSettingsName); + if (outputFile.exists()) + outputFile.setContents(inputStream, true, true, null); + else + outputFile.create(inputStream, true, null); + + inputStream.close(); + } + catch (CoreException e) { + Logger.logException(e); + throw new SimpleNodeOperator.WriteContentSettingsFailureException("invalid outputFile in writeDOMDocument()");//$NON-NLS-1$ + } + catch (IOException e) { + Logger.logException(e); + throw new SimpleNodeOperator.WriteContentSettingsFailureException("invalid outputStream or inputStream in writeDOMDocument()");//$NON-NLS-1$ + } + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsChangeSubject.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsChangeSubject.java new file mode 100644 index 0000000..7bc580b --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsChangeSubject.java
@@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public class ContentSettingsChangeSubject extends AbstractSubject { + + + private static INotify notify; + private static ISubject subject; + + protected static INotify getNotify() { + if (notify == null && subject == null) { + synchronized (ContentSettingsChangeSubject.class) { + if (notify == null && subject == null) { + notify = new ContentSettingsChangeSubject(); + subject = (ISubject) notify; + } + } + } + + return notify; + } + + public static ISubject getSubject() { + if (subject == null && notify == null) { + synchronized (ContentSettingsChangeSubject.class) { + if (subject == null && notify == null) { + subject = new ContentSettingsChangeSubject(); + notify = (INotify) subject; + } + } + } + return subject; + } + + public synchronized void addListener(IContentSettingsListener listener) { + super.addListener(listener); + } + + public synchronized void notifyListeners(org.eclipse.core.resources.IResource changedResource) { + super.notifyListeners(changedResource); + } + + public synchronized void removeListener(IContentSettingsListener listener) { + super.removeListener(listener); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsCreator.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsCreator.java new file mode 100644 index 0000000..9dd355c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsCreator.java
@@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public class ContentSettingsCreator { + + public final static IContentSettings create() { + return ContentSettings.getInstance(); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsFileHandler.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsFileHandler.java new file mode 100644 index 0000000..0c52604 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsFileHandler.java
@@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + + +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; + +/** +* @deprecated This is package protected so no one cares anyways. +*/ +class ContentSettingsFileHandler extends AbstractContentSettingsHandler { + + + + private Map properties; + + + private void getProperties(IResource file) { + properties = getContentSettings().getProperties(file); + } + + /* + * @see AbstractContentSettingsHandler#handleAdded() + */ + protected void handleAdded() { + super.handleAdded(); + + if (super.getDelta().getFlags() == 0) { + // pulldown memu->copy->create file without override, new file, + // import without override + // copy,new,import has same kind(=1) and flag(=0). + + } else if ((getDelta().getFlags() & IResourceDelta.MOVED_FROM) != 0) { + // pulldown menu-> rename without override,pulldown menu->move + // without override + // instead of this method,handleRemoved() works for this delta + + } + + } + + + /* + * @see AbstractContentSettingsHandler#handleChanged() + */ + protected void handleChanged() { + // edit + if (getDelta().getFlags() == IResourceDelta.CONTENT && (getDelta().getFlags() & IResourceDelta.REPLACED) == 0) { + super.handleChanged(); + + } else if (getDelta().getFlags() == IResourceDelta.CONTENT && (getDelta().getFlags() & IResourceDelta.REPLACED) != 0) { + // override as a result of copy or import + // in Web project, copy with override doesn't happen + + // override as move or rename + // handleRemoved() works for this delta + super.handleChanged(); + + } + + + } + + /* + * @see AbstractContentSettingsHandler#handleRemoved() + */ + protected void handleRemoved() { + super.handleRemoved(); + IFile deletedFile = null; + + // if entry exists then remove it. + if (getDelta().getFlags() == 0) { + // pulldown menu->delete + deletedFile = (IFile) getDelta().getResource(); + + if (deletedFile == null) + return; + getContentSettings().deleteAllProperties(deletedFile); + + getContentSettings().releaseCache(); + } + + else if ((getDelta().getFlags() & IResourceDelta.MOVED_TO) != 0) { + + // pulldown menu-> rename, pulldown menu->move + deletedFile = (IFile) getDelta().getResource(); + getProperties(deletedFile); + + // get destination IResource + IPath targetPath = getDelta().getMovedToPath(); + IWorkspaceRoot iwr = ResourcesPlugin.getWorkspace().getRoot(); + IResource targetFile = iwr.getFile(targetPath);//iwr.findMember(targetPath); + + // set property of destination file + getContentSettings().deleteAllProperties(targetFile); + setProperties(targetFile); + if (properties != null) + properties.clear(); + properties = null; + } + + if (deletedFile == null) + return; + getContentSettings().deleteAllProperties(deletedFile); + + getContentSettings().releaseCache(); + + } + + private void setProperties(IResource file) { + if (file.getFileExtension() == null) + return; + if (!(file.getFileExtension().equalsIgnoreCase("shtml")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("htm")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("html")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("jhtml")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("xhtml")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("jsp")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("css")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("jsf")//$NON-NLS-1$ + || file.getFileExtension().equalsIgnoreCase("jspf")))//$NON-NLS-1$ + return; + if (properties == null || properties.isEmpty()) + return; + getContentSettings().setProperties(file, properties); + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsSelfHandler.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsSelfHandler.java new file mode 100644 index 0000000..27aa2b8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsSelfHandler.java
@@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + + +import org.eclipse.core.resources.IResourceDelta; + +/** +* @deprecated This is package protected so no one cares anyways. +*/ +final class ContentSettingsSelfHandler extends AbstractContentSettingsHandler { + + private static INotify notify;// = new ContentSettingsChangedSubject(); + + + private final void getSubject() { + notify = ContentSettingsChangeSubject.getNotify(); + } + + /* + * @see AbstractContentSettingsHandler#handleAdded() + */ + protected void handleAdded() { + getSubject(); + if (super.getDelta().getFlags() == 0) { + // pulldown memu->copy->create file without override, new file, + // property page create .contentsettings + super.handleAdded(); + notify.notifyListeners(getDelta().getResource()); + + } else if ((getDelta().getFlags() & IResourceDelta.MOVED_FROM) != 0) { + // pulldown menu-> rename without override,pulldown menu->move + // without override + super.handleAdded(); + notify.notifyListeners(getDelta().getResource()); + + } + + } + + + /* + * @see AbstractContentSettingsHandler#handleChanged() + */ + protected void handleChanged() { + getSubject(); + if ((getDelta().getFlags() & IResourceDelta.CONTENT) != 0 && (getDelta().getFlags() & IResourceDelta.REPLACED) == 0) { + // result of edit, property page operate setProperty(apply or ok + // button) in .contentsettings + super.handleChanged(); + + notify.notifyListeners(getDelta().getResource()); + + } else if ((getDelta().getFlags() & IResourceDelta.CONTENT) != 0 && (getDelta().getFlags() & IResourceDelta.REPLACED) != 0) { + super.handleChanged(); + notify.notifyListeners(getDelta().getResource()); + + } + + + } + + /* + * @see AbstractContentSettingsHandler#handleRemoved() + */ + protected void handleRemoved() { + getSubject(); + if (getDelta().getFlags() == 0) { + // pulldown menu->delete + super.handleRemoved(); + notify.notifyListeners(getDelta().getResource()); + + } else if ((getDelta().getFlags() & IResourceDelta.MOVED_TO) != 0) { + // pulldown menu-> rename, pulldown menu->move + super.handleRemoved(); + notify.notifyListeners(getDelta().getResource()); + + } + + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsSynchronizer.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsSynchronizer.java new file mode 100644 index 0000000..e698b65 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ContentSettingsSynchronizer.java
@@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300434 - Make inner classes static where possible + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.wst.sse.core.internal.Logger; + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public class ContentSettingsSynchronizer implements IResourceChangeListener { + static class ContentSettingsVisitor implements IResourceDeltaVisitor { + // redefinition in ContentSettings.java + private String contentSettingsName = ContentSettings.getContentSettingsName(); //$NON-NLS-1$ + private IResourceChangeEvent fEvent; + private IContentSettingsHandler handler; + + public ContentSettingsVisitor(IResourceChangeEvent event) { + this.fEvent = event; + } + + /** + * @see IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) + */ + public boolean visit(IResourceDelta delta) { + if (delta == null) + return false; + IResource resource = delta.getResource(); + if (resource == null) + return false; + // parent folder or project + if (delta.getKind() == IResourceDelta.CHANGED && delta.getFlags() == 0) + return true; + final int resourceType = resource.getType(); + switch (resourceType) { + case IResource.PROJECT : + return false; + case IResource.FILE : + // whether resource is .contentsettings file or not + IProject project = delta.getResource().getProject(); + if (this.fEvent.getType() == IResourceChangeEvent.POST_CHANGE && resource.equals(project.getFile(this.contentSettingsName))) { + // new object for .contentsettings + handler = new ContentSettingsSelfHandler(); + } + else if (this.fEvent.getType() == IResourceChangeEvent.PRE_BUILD && resource.getFileExtension() != null) { + // TODO change to content type! + if (resource.getFileExtension().equalsIgnoreCase("shtml") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("htm") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("html") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("jhtml") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("xhtml") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("jsp") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("css") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("jsf") //$NON-NLS-1$ + || resource.getFileExtension().equalsIgnoreCase("jspf")) { //$NON-NLS-1$ + // new object for IFile + handler = new ContentSettingsFileHandler(); + handler.handle(delta); + handler = null; + return true; + } + } + else + return false; // true or false;motomoto true; + break; + case IResource.FOLDER : + return true; + default : + return true; + } +// final IResourceDelta fDelta = delta; +// final IContentSettingsHandler deltaHandler = this.handler; + // XXX FIXME + // I commented out following to move this to "model" + // packages. Since wasnt't hooked up/working anyway, I think + // ok for now, but need to re-work as Job, once we do implement + // settings for resources again. +// Display display = getDisplay(); +// if (display != null && !display.isDisposed()) { +// display.asyncExec(new Runnable() { +// public void run() { +// if (deltaHandler != null) { +// deltaHandler.handle(fDelta); +// } +// } +// }); +// } +// else if (deltaHandler != null) { +// deltaHandler.handle(fDelta); +// } + handler = null; + return true; // true or false;motomoto true; + } + } + + private static ContentSettingsSynchronizer instance = null; + + public synchronized static ContentSettingsSynchronizer getInstance() { + if (instance == null) + instance = new ContentSettingsSynchronizer(); + return instance; + } + + private ContentSettingsVisitor csVisitor; + private boolean fListening = false; + private IProject fProject; + + + private final IProject getProject() { + return fProject; + } + + private final IWorkspace getWorkspace() { + if (getProject() == null) + return ResourcesPlugin.getWorkspace(); + return getProject().getWorkspace(); + } + + public synchronized void install() { + if (!fListening) { + getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.PRE_BUILD | IResourceChangeEvent.POST_CHANGE); + fListening = true; + } + } + + /** + * @return + */ + public synchronized boolean isListening() { + return fListening; + } + + /* + * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent) + */ + public void resourceChanged(IResourceChangeEvent event) { + IResourceDelta delta = event.getDelta(); + if (delta != null) { + try { + if (csVisitor == null) + csVisitor = new ContentSettingsVisitor(event); + delta.accept(csVisitor); + } + catch (CoreException e) { + Logger.logException(e); + } + } + csVisitor = null; + } + + public synchronized void unInstall() { + getWorkspace().removeResourceChangeListener(this); + fListening = false; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettings.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettings.java new file mode 100644 index 0000000..058dd5c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettings.java
@@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + + +import java.util.Map; + +import org.eclipse.core.resources.IResource; + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public interface IContentSettings { + /** + * + */ + public final String CSS_PROFILE = "css-profile"; //$NON-NLS-1$ + /** + * + */ + public final String DEVICE_PROFILE = "target-device"; //$NON-NLS-1$ + + /** + * + */ + public final String DOCUMENT_TYPE = "document-type"; //$NON-NLS-1$ + /** + * + */ + public final String HTML_DOCUMENT_TYPE = "html-document-type"; //$NON-NLS-1$ + + /** + * + */ + public void deleteAllProperties(final IResource deletedFile); + + /** + * + */ + public void deleteProperty(final IResource resource, final String propertyName); + + /** + * + */ + public boolean existsProperties(IResource resource); + + /** + * + */ + public Map getProperties(final IResource resource); + + /** + * + */ + public String getProperty(final IResource resource, final String propertyName); + + /** + * release cache of DOM tree in .contentsettings + */ + public void releaseCache(); + + /** + * + */ + public void setProperties(final IResource resource, final Map properties); + + /** + * + */ + public void setProperty(final IResource resource, final String propertyName, final String propertyValue); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettingsHandler.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettingsHandler.java new file mode 100644 index 0000000..8ef8b45 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettingsHandler.java
@@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + + +import org.eclipse.core.resources.IResourceDelta; + +/** +* @deprecated This is package protected so no one cares anyways. +*/ +interface IContentSettingsHandler { + + public void handle(final IResourceDelta delta); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettingsListener.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettingsListener.java new file mode 100644 index 0000000..41151f8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/IContentSettingsListener.java
@@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public interface IContentSettingsListener { + + void contentSettingsChanged(org.eclipse.core.resources.IResource e); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/INotify.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/INotify.java new file mode 100644 index 0000000..4dcf5fe --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/INotify.java
@@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + +/** +* @deprecated This is package protected so no one cares anyways. +*/ +interface INotify { + + void notifyListeners(org.eclipse.core.resources.IResource resource); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ISubject.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ISubject.java new file mode 100644 index 0000000..4ff4030 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/ISubject.java
@@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + +/** + * @deprecated See + * org.eclipse.html.core.internal.contentproperties.HTMLContentProperties + */ +public interface ISubject { + + void addListener(IContentSettingsListener listener); + + void removeListener(IContentSettingsListener listener); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/SimpleNodeOperator.java b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/SimpleNodeOperator.java new file mode 100644 index 0000000..ace7a38 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-contentproperties/org/eclipse/wst/sse/internal/contentproperties/SimpleNodeOperator.java
@@ -0,0 +1,354 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300434 - Make inner classes static where possible + *******************************************************************************/ +package org.eclipse.wst.sse.internal.contentproperties; + + + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.eclipse.wst.sse.core.internal.Logger; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeIterator; +import org.xml.sax.SAXException; + +/** +* @deprecated This is package protected so no one cares anyways. +*/ +class SimpleNodeOperator { + + static class CreateContentSettingsFailureException extends Exception { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + public CreateContentSettingsFailureException(String reason) { + super(reason); + } + } + + + static class ReadContentSettingsFailureException extends Exception { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + public ReadContentSettingsFailureException(String reason) { + super(reason); + } + } + + static class WriteContentSettingsFailureException extends Exception { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + public WriteContentSettingsFailureException(String reason) { + super(reason); + } + } + + // writer class for .contentSettings. + static class XMLDocumentWriter { + OutputStream fOut; + + protected XMLDocumentWriter(OutputStream out) { + this.fOut = out; + } + + protected final void close() { + try { + fOut.close(); + } catch (IOException e) { + // do nothing, shouldn't matter + } + } + + protected void serialize(Document sourceDocument) throws WriteContentSettingsFailureException { + // JAXP transformation + Source domSource = new DOMSource(sourceDocument); + try { + Transformer serializer = TransformerFactory.newInstance().newTransformer(); + try { + serializer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ + serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IllegalArgumentException e) { + // unsupported properties + } + serializer.transform(domSource, new StreamResult(fOut)); + } catch (TransformerConfigurationException e) { + throw new WriteContentSettingsFailureException(e.getMessage()); + } catch (TransformerFactoryConfigurationError e) { + throw new WriteContentSettingsFailureException(e.getMessage()); + } catch (TransformerException e) { + throw new WriteContentSettingsFailureException(e.getMessage()); + } + } + } + + public static void main(String[] args) { + SimpleNodeOperator a = null; + try { + a = new SimpleNodeOperator("workspace/org.eclipse.examples.contentsettings/.contentsettings.xml");//$NON-NLS-1$ + } catch (Exception e) { + System.exit(0); + } + + // print all Elements + //a.printTree(iter); + + // add Element + Map attMap = new Hashtable(); + attMap.put("path", "hogepath");//$NON-NLS-1$ //$NON-NLS-2$ + attMap.put("fDocument-type", "documenthogehoge");//$NON-NLS-1$ //$NON-NLS-2$ + a.addElementUnderRoot("file", attMap);//$NON-NLS-1$ + + try { + a.writeDocument(System.out); + } catch (Exception e) { + System.err.println(e.toString()); + } + + } + + //never used + //private DOMParser parser; + private Document fDocument; + private Node root; + + private String settingsFileName; + + + public SimpleNodeOperator(Document doc) throws CreateContentSettingsFailureException { + + if (doc == null) + throw new CreateContentSettingsFailureException("Document doc==null");//$NON-NLS-1$ + fDocument = doc; + root = fDocument.getLastChild(); + if (root == null) + throw new CreateContentSettingsFailureException("Node root==null");//$NON-NLS-1$ + } + + public SimpleNodeOperator(String fullPath) throws ReadContentSettingsFailureException { + this.settingsFileName = fullPath; + createObjectOfDocument(); + } + + // add attribute(attName=attValue) of ele without checking overlapping of + // another attributes of ele. + // if overlapping ,override + protected Node addAttributeAt(Element ele, String attName, String attValue) { + Attr att = fDocument.createAttribute(attName); + att.setValue(attValue); + if (ele != null) + ele.setAttributeNode(att); + return ele; + } + + protected Node addElementUnder(Node parent, String tagName, Map attMap) { + if (parent == null || tagName == null) + return null; + Element e = fDocument.createElement(tagName); + if (attMap != null) { + if (!attMap.isEmpty()) { + Set attKeys = attMap.keySet(); + Iterator iter = attKeys.iterator(); + while (iter.hasNext()) { + String key = (String) iter.next(); + e.setAttribute(key, (String) attMap.get(key)); + } + } + } + parent.appendChild(e); + return e; + } + + protected final Node addElementUnderRoot(String tagName) { + return addElementUnder(root, tagName, null); + } + + // add element with attMap as attribute without checking overlapping. + protected final Node addElementUnderRoot(String tagName, Map attMap) { + return addElementUnder(root, tagName, attMap); + } + + private void createObjectOfDocument() throws ReadContentSettingsFailureException { + try { + fDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(settingsFileName)); + } catch (SAXException e) { + Logger.logException("exception parsing" + settingsFileName, e); //$NON-NLS-1$ + } catch (IOException e) { + Logger.logException("I/O exception parsing" + settingsFileName, e); //$NON-NLS-1$ + } catch (ParserConfigurationException e) { + Logger.logException("exception accessing DOMImplementation", e); //$NON-NLS-1$ + } catch (FactoryConfigurationError e) { + Logger.logException("exception accessing DOMImplementation", e); //$NON-NLS-1$ + } + //get the root of the XML fDocument + root = fDocument.getLastChild(); + if (root == null) { + throw new ReadContentSettingsFailureException("Error: Node root==null");//$NON-NLS-1$ + } + } + + protected Map getAttributesOf(Node node) { + if (!node.hasAttributes()) + return null; + Map map = new HashMap(); + NamedNodeMap attrs = node.getAttributes(); + int size = attrs.getLength(); + for (int i = 0; i < size; i++) { + Attr attr = (Attr) attrs.item(i); + map.put(attr.getName(), attr.getValue()); + } + return (map); + } + + private Node getElementWithAttribute(Node first, String attName, String attValue) { + Node navpoint = first; + while (navpoint != null) { + if (navpoint.getNodeType() == Node.ELEMENT_NODE) { + NamedNodeMap m = navpoint.getAttributes(); + if (m == null) + continue; + if (m.getNamedItem(attName) != null) { + if (attValue.equals(((Attr) m.getNamedItem(attName)).getNodeValue())) + return navpoint; + } + NodeList childNodes = navpoint.getChildNodes(); + if (childNodes != null && childNodes.getLength() > 0) { + Node holdNode = getElementWithAttribute(navpoint.getFirstChild(), attName, attValue); + if (holdNode != null) { + return holdNode; + } + } + } + navpoint = navpoint.getNextSibling(); + } + return null; + } + + + // return a (first) Element with attr(attName=attValue) it if exists, + // otherwise return null + protected Node getElementWithAttribute(String attName, String attValue) { + if (attName == null || attValue == null || !fDocument.hasChildNodes()) + return null; + return getElementWithAttribute(fDocument.getFirstChild(), attName, attValue); + } + + // retrun Element which has nodeName as Node Name + protected Node getElementWithNodeName(String nodeName) { + if (nodeName == null) + return null; + NodeList nodes = fDocument.getElementsByTagName(nodeName); + if (nodes.getLength() > 0) { + return nodes.item(0); + } + return null; + } + + public void printTree(NodeIterator iter) { + Node n; + while ((n = iter.nextNode()) != null) { + System.out.println(n.getNodeName() + ":");//$NON-NLS-1$ + NamedNodeMap m = n.getAttributes(); + if (m == null) + continue; + for (int i = 0; i < m.getLength(); i++) { + String attName = m.item(i).getNodeName(); + System.out.print(" " + attName + "=" + m.item(i).getNodeValue());//$NON-NLS-1$ //$NON-NLS-2$ + } + System.out.println("");//$NON-NLS-1$ + } + } + + + // remove attribute(attName) at ele. + protected Attr removeAttributeAt(Element ele, String attName) { + if (ele == null || attName == null) + return null; + Attr att = ele.getAttributeNode(attName); + ele.removeAttribute(attName); + return att; + } + + protected Element removeElementWith(String nodeName) { + NodeList nodes = fDocument.getElementsByTagName(nodeName); + for (int i = 0; i < nodes.getLength(); i++) { + nodes.item(i).getParentNode().removeChild(nodes.item(i)); + } + return null; + } + + // remove a (first) Element with attr(attName=attValue) and return it if + // exists, otherwise return null + protected Element removeElementWith(String attName, String attValue) { + if (fDocument.hasChildNodes()) { + Node element = getElementWithAttribute(attName, attValue); + if (element != null && element.getNodeType() == Node.ELEMENT_NODE) { + element.getParentNode().removeChild(element); + return (Element) element; + } + } + return null; + + } + + // update attribute(attName=newValue) at ele if both ele and attribute of + // ele exist + protected void updateAttributeAt(Element ele, String attName, String newValue) { + Attr att = null; + if (ele != null) + if ((att = ele.getAttributeNode(attName)) != null) + att.setValue(newValue); + } + + protected void writeDocument(OutputStream out) throws WriteContentSettingsFailureException { + XMLDocumentWriter writer = new XMLDocumentWriter(out); + try { + writer.serialize(fDocument); + } finally { + writer.close(); + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedIO.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedIO.java new file mode 100644 index 0000000..1f31829 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedIO.java
@@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.util.Properties; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.wst.sse.core.internal.encoding.util.Assert; +import org.eclipse.wst.sse.core.internal.encoding.util.Logger; +import org.osgi.framework.Bundle; + + +public abstract class CodedIO { + + private final boolean DEBUG = false; + + public static final int MAX_BUF_SIZE = 1024 * 8; + + public static final int MAX_MARK_SIZE = MAX_BUF_SIZE; + + public static final String NO_SPEC_DEFAULT = "NoSpecDefault"; //$NON-NLS-1$ + + private static Properties overridenCharsets = null; + + /** + * <p> + * There are two well known understood cases where the standard/default + * Java Mappings are not sufficient. (Thanks to Hirotaka Matsumoto for + * providing these two). I believe there are others that individual + * customers have requested to override on a case by case basis, but I've + * lost the details. TODO-future: document some of those use-cases. + * </p> + * <ul> + * <li>ISO-8859-8-I</li> + * <p> + * In the code conversion point of view, ISO-9959-8 and ISO-8859-8-I are + * the same. However. the representation on the browser is different. ( + * It's very very hard to explain this into the words, but once you will + * see, you will understand it :) Many BiDi HTML/JSPs use ISO-8859-8-I in + * META/page directive. So WSAD needs to support this encoding. + * </p> + * <li>X-SJIS</li> + * <p> + * Because Mosaic/Navigator 2.0 supported only X-SJIS/X-EUC-JP, lots of + * old HTML files used X-SJIS/X-EUC-JP so that the customers still want us + * to support this code conversion for HTML files. + * </p> + * </ul> + * + * @param detectedCharsetName + * @return the detectedCharsetName, if no overrides, otherwise the charset + * name that should be used instead of detectedCharsetName + */ + /** + * This method is deliberatly 'default access' since clients should not + * need to access this information directly. + */ + static public String checkMappingOverrides(String detectedCharsetName) { + // This method MUST return what was passed in, if + // there are no + // overrides. + String result = detectedCharsetName; + String newResult = getOverridenCharsets().getProperty(detectedCharsetName); + if (newResult != null) { + result = newResult; + } + return result; + } + + /** + * Note: once this instance is created, trace info still needs to be + * appended by caller, depending on the context its created. + */ + public static EncodingMemento createEncodingMemento(byte[] detectedBom, String javaCharsetName, String detectedCharsetName, String unSupportedName, String specDefaultEncoding, String reason) { + EncodingMemento result = new EncodingMemento(); + result.setJavaCharsetName(javaCharsetName); + result.setDetectedCharsetName(detectedCharsetName); + // TODO: if detectedCharset and spec default is + // null, need to use "work + // bench based" defaults. + if (specDefaultEncoding == null) + result.setAppropriateDefault(NO_SPEC_DEFAULT); + else + result.setAppropriateDefault(specDefaultEncoding); + if (unSupportedName != null) { + result.setInvalidEncoding(unSupportedName); + } + // check if valid + try { + Charset.isSupported(javaCharsetName); + } catch (IllegalCharsetNameException e) { + result.setInvalidEncoding(javaCharsetName); + } + + // check UTF83ByteBOMUsed and UnicodeStream + if (detectedBom != null) { + if (detectedBom.length == 2) + result.setUnicodeStream(true); + else if (detectedBom.length == 3) + result.setUTF83ByteBOMUsed(true); + result.setUnicodeBOM(detectedBom); + } + return result; + } + + /** + * Note: once this instance is created, trace info still needs to be + * appended by caller, depending on the context its created. + */ + public static EncodingMemento createEncodingMemento(String detectedCharsetName) { + return createEncodingMemento(detectedCharsetName, null); + } + + /** + * Note: once this instance is created, trace info still needs to be + * appended by caller, depending on the context its created. + */ + public static EncodingMemento createEncodingMemento(String detectedCharsetName, String reason) { + return createEncodingMemento(detectedCharsetName, reason, null); + } + + /** + * Note: once this instance is created, trace info still needs to be + * appended by caller, depending on the context its created. + */ + public static EncodingMemento createEncodingMemento(String detectedCharsetName, String reason, String specDefaultEncoding) { + EncodingMemento result = new EncodingMemento(); + result = new EncodingMemento(); + String javaCharset = getAppropriateJavaCharset(detectedCharsetName); + result.setJavaCharsetName(javaCharset); + result.setDetectedCharsetName(detectedCharsetName); + // TODO: if detectedCharset and spec default is + // null, need to use "work + // bench based" defaults. + if (specDefaultEncoding == null) + result.setAppropriateDefault(NO_SPEC_DEFAULT); + else + result.setAppropriateDefault(specDefaultEncoding); + // check if valid + try { + Charset.isSupported(javaCharset); + } catch (IllegalCharsetNameException e) { + result.setInvalidEncoding(javaCharset); + } + + return result; + } + + /** + * This method can return null, if invalid charset name (in which case + * "appropriateDefault" should be used, if a name is really need for some + * "save anyway" cases). + * + * @param detectedCharsetName + * @return + */ + public static String getAppropriateJavaCharset(String detectedCharsetName) { + // we don't allow null argument (or risk NPE or + // IllegalArgumentException later at several + // points. + Assert.isNotNull(detectedCharsetName, "illegal charset argument. it can not be null"); //$NON-NLS-1$ + String result = detectedCharsetName; + // 1. Check explicit mapping overrides from + // property file + result = CodedIO.checkMappingOverrides(detectedCharsetName); + // 2. Use the "canonical" name from JRE mappings + // Note: see Charset JavaDoc, the name you get one + // with can be alias, + // the name you get back is "standard" name. + Charset javaCharset = null; + // Note: this will immediatly throw + // "UnsuppotedCharsetException" if it + // invalid. Issue: Is it more client friendly to + // eat that exception and return null? + javaCharset = Charset.forName(result); + if (javaCharset != null) { + result = javaCharset.name(); + } + return result; + } + + /** + * @return Returns the overridenCharsets. + */ + private static Properties getOverridenCharsets() { + if (overridenCharsets == null) { + overridenCharsets = new Properties(); + Bundle keyBundle = Platform.getBundle(ICodedResourcePlugin.ID); + IPath keyPath = new Path("config/override.properties"); //$NON-NLS-1$ + URL location = Platform.find(keyBundle, keyPath); + InputStream propertiesInputStream = null; + try { + propertiesInputStream = location.openStream(); + overridenCharsets.load(propertiesInputStream); + } catch (IOException e) { + // if can't read, just assume there's no + // overrides + // and repeated attempts will not occur, + // since they + // will be represented by an empty + // Properties object + } + } + return overridenCharsets; + } + + /** + * This class need not be instantiated (though its subclasses can be). + */ + protected CodedIO() { + super(); + } + + protected EncodingMemento createMemento(IContentDescription contentDescription) { + EncodingMemento result; + String appropriateDefault = contentDescription.getContentType().getDefaultCharset(); + String detectedCharset = (String) contentDescription.getProperty(IContentDescriptionExtended.DETECTED_CHARSET); + String unSupportedCharset = (String) contentDescription.getProperty(IContentDescriptionExtended.UNSUPPORTED_CHARSET); + String javaCharset = contentDescription.getCharset(); + // integrity checks for debugging + if (javaCharset == null) { + Logger.log(Logger.INFO_DEBUG, "charset equaled null!"); //$NON-NLS-1$ + } else if (javaCharset.length() == 0) { + Logger.log(Logger.INFO_DEBUG, "charset equaled emptyString!"); //$NON-NLS-1$ + } + byte[] BOM = (byte[]) contentDescription.getProperty(IContentDescription.BYTE_ORDER_MARK); + //result = (EncodingMemento) + // contentDescription.getProperty(IContentDescriptionExtended.ENCODING_MEMENTO); + result = createEncodingMemento(BOM, javaCharset, detectedCharset, unSupportedCharset, appropriateDefault, null); + if (!result.isValid()) { + result.setAppropriateDefault(appropriateDefault); + // integrity check for debugging "invalid" cases. + // the apprriate default we have, should equal what's in the + // detected field. (not sure this is always required) + if (DEBUG && appropriateDefault != null && !appropriateDefault.equals(detectedCharset)) { + Logger.log(Logger.INFO_DEBUG, "appropriate did not equal detected, as expected for invalid charset case"); //$NON-NLS-1$ + } + } + return result; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedReaderCreator.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedReaderCreator.java new file mode 100644 index 0000000..155ccdb --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedReaderCreator.java
@@ -0,0 +1,542 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; +import org.eclipse.wst.sse.core.internal.encoding.util.Assert; +import org.eclipse.wst.sse.core.internal.encoding.util.BufferedLimitedStream; +import org.eclipse.wst.sse.core.internal.encoding.util.Logger; +import org.eclipse.wst.sse.core.internal.encoding.util.NullInputStream; +import org.eclipse.wst.sse.core.internal.encoding.util.UnicodeBOMEncodingDetector; +import org.eclipse.wst.sse.core.internal.exceptions.UnsupportedCharsetExceptionWithDetail; + + +/** + * The purpose of this class is to centralize analysis of a file to determine + * the most appropriate rules of decoding it. The intended use is to set the + * input, then get the reader for that input which will have its encoding set + * appropriately. Additionally, there is an EncodingMemento provided, which + * will be required, in some cases, to later determine the most appropriate + * form of encoded output. + */ +public class CodedReaderCreator extends CodedIO { + + + private boolean fClientSuppliedStream; + + + private EncodingMemento fEncodingMemento; + + private EncodingRule fEncodingRule; + + private String fFilename; + + private IFile fIFile; + + + private InputStream fInputStream; + + private static final String CHARSET_UTF_16= "UTF-16"; //$NON-NLS-1$ + + private static final String CHARSET_UTF_16LE= "UTF-16LE"; //$NON-NLS-1$ + + public CodedReaderCreator() { + + super(); + } + + public CodedReaderCreator(IFile file) throws CoreException, IOException { + + this(); + set(file); + setEncodingRule(EncodingRule.CONTENT_BASED); + } + + public CodedReaderCreator(IFile file, EncodingRule encodingRule) throws CoreException, IOException { + + this(); + set(file); + setEncodingRule(encodingRule); + } + + public CodedReaderCreator(String filename, InputStream inputStream) { + + this(); + set(filename, inputStream); + setEncodingRule(EncodingRule.CONTENT_BASED); + } + + public CodedReaderCreator(String filename, InputStream inputStream, EncodingRule encodingRule) { + + this(); + set(filename, inputStream); + setEncodingRule(encodingRule); + } + + private EncodingMemento checkForEncodingInContents(InputStream limitedStream) throws CoreException, IOException { + EncodingMemento result = null; + + // if encoding memento already set, then iFile must + // have been set, and no need to get again. + if (fEncodingMemento != null) { + result = fEncodingMemento; + } + else { + if (fClientSuppliedStream) { + try { + limitedStream.reset(); + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); + IContentDescription contentDescription = contentTypeManager.getDescriptionFor(limitedStream, fFilename, IContentDescription.ALL); + if (contentDescription != null) { + fEncodingMemento = createMemento(contentDescription); + } + result = fEncodingMemento; + } + finally { + limitedStream.reset(); + } + } + else { + // throw new IllegalStateException("unexpected state: + // encodingMemento was null but no input stream supplied by + // client"); //$NON-NLS-1$ + result = null; + } + } + + if (result != null && !result.isValid() && !forceDefault()) { + throw new UnsupportedCharsetExceptionWithDetail(result); + } + + return result; + } + + /** + * @param resettableLimitedStream + */ + private EncodingMemento checkStreamForBOM(InputStream resettableLimitedStream) { + EncodingMemento result = null; + UnicodeBOMEncodingDetector unicodeBOMEncodingDetector = new UnicodeBOMEncodingDetector(); + unicodeBOMEncodingDetector.set(resettableLimitedStream); + result = unicodeBOMEncodingDetector.getEncodingMemento(); + return result; + } + + /** + * @param iFile + * @throws CoreException + * @throws IOException + */ + private EncodingMemento findMementoFromFileCase() throws CoreException, IOException { + EncodingMemento result = null; + IContentDescription contentDescription = null; + try { + // This method provides possible improved performance at the + // cost of sometimes returning null + if (fIFile.exists()) + contentDescription = fIFile.getContentDescription(); + } + catch (CoreException e) { + // Assume if core exception occurs, we can still try more + // expensive + // discovery options. + Logger.logException(e); + } + if (contentDescription == null && fIFile.isAccessible()) { + InputStream contents = null; + try { + contents = fIFile.getContents(); + contentDescription = Platform.getContentTypeManager().getDescriptionFor(contents, fIFile.getName(), IContentDescription.ALL); + } + catch (CoreException e1) { + // Assume if core exception occurs, we can't really do much + // with + // determining encoding, etc. + Logger.logException(e1); + throw e1; + } + catch (IOException e2) { + // We likely couldn't get the contents of the file, something + // is really wrong + Logger.logException(e2); + throw e2; + } + if (contents != null) { + try { + contents.close(); + } + catch (IOException e2) { + Logger.logException(e2); + } + } + } + if (contentDescription != null) { + result = createMemento(contentDescription); + } + + return result; + } + + /** + * The primary method which contains the highest level rules for how to + * decide appropriate decoding rules: 1. first check for unicode stream 2. + * then looked for encoding specified in content (according to the type of + * content that is it ... xml, html, jsp, etc. 3. then check for various + * settings: file settings first, if null check project settings, if null, + * check user preferences. 4. lastly (or, what is the last user + * preference) is to use "workbench defaults". + * + * @throws IOException + * @throws CoreException + */ + private EncodingMemento findMementoFromStreamCase() throws CoreException, IOException { + + EncodingMemento result = null; + InputStream resettableLimitedStream = null; + try { + resettableLimitedStream = getLimitedStream(getResettableStream()); + if (resettableLimitedStream != null) { + // first check for unicode stream + result = checkStreamForBOM(resettableLimitedStream); + // if not that, then check contents + if (result == null) { + resettableLimitedStream.reset(); + result = checkForEncodingInContents(resettableLimitedStream); + } + + } + else { + // stream null, may name's not. + if (fFilename != null) { + // filename not null + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); + IContentDescription contentDescription = contentTypeManager.getDescriptionFor(new NullInputStream(), fFilename, IContentDescription.ALL); + if (contentDescription != null) { + result = createMemento(contentDescription); + } + } + } + } + finally { + if (resettableLimitedStream != null) { + handleStreamClose(resettableLimitedStream); + } + } + return result; + } + + private boolean forceDefault() { + + boolean result = false; + if (fEncodingRule != null && fEncodingRule == EncodingRule.FORCE_DEFAULT) + result = true; + return result; + } + + public Reader getCodedReader() throws CoreException, IOException { + + Reader result = null; + // we make a local copy of encoding memento so + // stream won't + // be accessed simultaneously. + EncodingMemento encodingMemento = getEncodingMemento(); + Assert.isNotNull(encodingMemento, "Appears reader requested before file or stream set"); //$NON-NLS-1$ + InputStream streamToReturn = getResettableStream(); + streamToReturn.reset(); + // if UTF 3 byte BOM is used (or UTF-16LE), the + // built in converters + // don't + // correct skip all three bytes ... so skip + // remaining one to leave + // stream transparently ready for client. + // see ... TODO look up bug number + if (encodingMemento.isUnicodeStream()) { + streamToReturn.skip(2); + } + else if (encodingMemento.isUTF83ByteBOMUsed()) { + streamToReturn.skip(3); + } + String charsetName = encodingMemento.getJavaCharsetName(); + if (charsetName == null) { + charsetName = encodingMemento.getDetectedCharsetName(); + } + if (!encodingMemento.isValid() && !forceDefault()) { + throw new UnsupportedCharsetExceptionWithDetail(encodingMemento); + } + + if (fEncodingRule == EncodingRule.FORCE_DEFAULT) { + charsetName = encodingMemento.getAppropriateDefault(); + } + + // [228366] For files that have a unicode BOM, and a charset name of UTF-16, the charset decoder needs "UTF-16LE" + if(CHARSET_UTF_16.equals(charsetName) && encodingMemento.getUnicodeBOM() == IContentDescription.BOM_UTF_16LE) + charsetName = CHARSET_UTF_16LE; + + Charset charset = Charset.forName(charsetName); + CharsetDecoder charsetDecoder = charset.newDecoder(); + if (fEncodingRule == EncodingRule.IGNORE_CONVERSION_ERROR) { + charsetDecoder.onMalformedInput(CodingErrorAction.REPLACE); + charsetDecoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } + else { + charsetDecoder.onMalformedInput(CodingErrorAction.REPORT); + charsetDecoder.onUnmappableCharacter(CodingErrorAction.REPORT); + } + // more efficient to be buffered, and I know of no + // reason not to return + // that directly. + result = new BufferedReader(new InputStreamReader(streamToReturn, charsetDecoder), CodedIO.MAX_BUF_SIZE); + result.mark(CodedIO.MAX_BUF_SIZE); + return result; + } + + public EncodingMemento getEncodingMemento() throws CoreException, IOException { + // figure out encoding memento from encoding strategy + if (fEncodingMemento == null) { + if (fClientSuppliedStream) { + fEncodingMemento = findMementoFromStreamCase(); + } + else if (fIFile != null) { + fEncodingMemento = findMementoFromFileCase(); + } + } + + // if encoding stratagy doesn't provide answer, + // then try file settings, project settings, + // user preferences, and + // finally workbench default. + // + if (fEncodingMemento == null || fEncodingMemento.getDetectedCharsetName() == null) { + fEncodingMemento = getEncodingMementoFromResourceAndPreference(); + } + + // use DefaultNameRules from NonContentBasedEncodingRules as the final + // default + if (fEncodingMemento == null) { + fEncodingMemento = handleNotProvidedFromContentCase(); + } + + return fEncodingMemento; + } + + /* + * This method is called only when encoding is not detected in the file. + * + * Here is encoding lookup order we will try: - try resource content + * description (Eclipse Text file encoding) - try resource content + * properties (for JSP only) - try content type encoding preferences (for + * HTML only) - try resource content description (Eclipse Text file + * encoding, implicit check) + * + * Note: This method appears in both CodedReaderCreator and + * CodedStreamCreator (with just a minor difference). They should be kept + * the same. + */ + private EncodingMemento getEncodingMementoFromResourceAndPreference() throws IOException, CoreException { + EncodingMemento encodingMemento = fEncodingMemento; + + // Follow Eclipse Platform's direction. Get the charset from IFile. + if (fIFile != null) { + String charset = fIFile.getCharset(); + encodingMemento = CodedIO.createEncodingMemento(charset); + } + + return encodingMemento; + } + + /** + * Ensures that an InputStream has mark/reset support, is readlimit is + * set, and that the stream is "limitable" (that is, reports "end of + * input" rather than allow going past mark). This is very specialized + * stream introduced to overcome + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=67211. See also + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=68565 + */ + private InputStream getLimitedStream(InputStream original) { + if (original == null) + return null; + if (original instanceof BufferedLimitedStream) + return original; + InputStream s = new BufferedLimitedStream(original, CodedIO.MAX_MARK_SIZE); + s.mark(CodedIO.MAX_MARK_SIZE); + return s; + } + + private InputStream getResettableStream() throws CoreException, IOException { + + InputStream resettableStream = null; + + if (fIFile != null) { + InputStream inputStream = null; + try { + // note we always get contents, even if out of synch + inputStream = fIFile.getContents(true); + } + catch (CoreException e) { + // SHOULD actually check for existence of + // fIStorage, but + // for now will just assume core exception + // means it + // doesn't exist on file system, yet. + // and we'll log, just in case its a noteable error + Logger.logException(e); + inputStream = new NullInputStream(); + } + resettableStream = new BufferedInputStream(inputStream, CodedIO.MAX_BUF_SIZE); + } + else { + if (fInputStream != null) { + if (fInputStream.markSupported()) { + resettableStream = fInputStream; + // try { + resettableStream.reset(); + // } + // catch (IOException e) { + // // assumed just hasn't been marked yet, so ignore + // } + } + else { + resettableStream = new BufferedInputStream(fInputStream, CodedIO.MAX_BUF_SIZE); + } + } + } + + if (resettableStream == null) { + resettableStream = new NullInputStream(); + } + + // mark this once, stream at "zero" position + resettableStream.mark(MAX_MARK_SIZE); + return resettableStream; + } + + private EncodingMemento handleNotProvidedFromContentCase() { + + EncodingMemento result = null; + String specDefault = null; + // try { + // specDefault = getEncodingDetector().getSpecDefaultEncoding(); + // } + // catch (CoreException e) { + // // If this exception occurs, assumes there is + // // no specDefault + // } + // catch (IOException e) { + // // If this exception occurs, assumes there is + // // no specDefault + // } + // finally { + // try { + // handleStreamClose(fEncodingDetectorStream); + // } + // catch (IOException e1) { + // // severe error, not much to do here + // } + // } + // this logic should be moved to 'detection' if not already + String charset = NonContentBasedEncodingRules.useDefaultNameRules(specDefault); + Assert.isNotNull(charset, "post condition failed"); //$NON-NLS-1$ + result = CodedIO.createEncodingMemento(charset); + return result; + } + + /** + * @param resettableInputStream + * @throws IOException + */ + private void handleStreamClose(InputStream resettableInputStream) throws IOException { + + if (resettableInputStream != null) { + if (fClientSuppliedStream) { + resettableInputStream.reset(); + } + else { + + resettableInputStream.close(); + } + } + } + + // TODO We just copy the content properties encoding to current resource's + // encoding for now. May improve the UI later by setting an informational + // message and/or disable the content properties encoding field. + // TODO: remake private else remove + void migrateContentPropertiesEncoding(String encoding) throws CoreException { + final IFile file = fIFile; + final String charset = encoding; + // TODO: externalize string later + Job migrater = new Job(SSECoreMessages.Migrate_Charset) { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (file != null) { + try { + file.setCharset(charset, null); + } + catch (CoreException e) { + Logger.logException(e); + } + } + return Status.OK_STATUS; + } + }; + migrater.setSystem(true); + migrater.schedule(); + } + + private void resetAll() { + + fEncodingRule = null; + fIFile = null; + fFilename = null; + fInputStream = null; + fEncodingMemento = null; + fClientSuppliedStream = false; + } + + public void set(IFile iFile) throws CoreException, IOException { + Assert.isNotNull(iFile, "illegal argument"); //$NON-NLS-1$ + resetAll(); + fIFile = iFile; + } + + public void set(String filename, InputStream inputStream) { + + resetAll(); + fFilename = filename; + fInputStream = inputStream; + fClientSuppliedStream = true; + } + + public void setEncodingRule(EncodingRule encodingRule) { + + fEncodingRule = encodingRule; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedStreamCreator.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedStreamCreator.java new file mode 100644 index 0000000..ac1224c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CodedStreamCreator.java
@@ -0,0 +1,509 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.UnmappableCharacterException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.internal.encoding.util.Assert; +import org.eclipse.wst.sse.core.internal.encoding.util.Logger; +import org.eclipse.wst.sse.core.internal.exceptions.CharConversionErrorWithDetail; +import org.eclipse.wst.sse.core.internal.exceptions.MalformedOutputExceptionWithDetail; +import org.eclipse.wst.sse.core.internal.exceptions.UnsupportedCharsetExceptionWithDetail; + + +public class CodedStreamCreator extends CodedIO { + + private final static int INITIAL_BUFFER_SIZE = 1024 * 16; + + // the 32 bytes used by default by ByteOutputStream is + // a little small + private static final String PROGRAM_ERROR__FAILED_TO_FIND_ANY_CHARSET_ANYWHERE_ = "Program error: failed to find any charset anywhere!"; //$NON-NLS-1$ + + private static final String UTF_16BE_CHARSET_NAME = "UTF-16BE"; //$NON-NLS-1$ + private static final String UTF_16LE_CHARSET_NAME = "UTF-16LE"; //$NON-NLS-1$ + // private static final String UTF_16_CHARSET_NAME = "UTF-16"; + // //$NON-NLS-1$ + + private static final String UTF_8_CHARSET_NAME = "UTF-8"; //$NON-NLS-1$ + + private boolean fClientSuppliedReader; + + // future_TODO: this 'checkConversion' can be a little + // pricey for large + // files, chould be a user preference, or something. + // private static final boolean checkConversion = true; + private EncodingMemento fCurrentEncodingMemento; + + private EncodingMemento fEncodingMemento; + + private String fFilename; + + private boolean fHasBeenAnalyzed; + + private IFile fIFile; + + private EncodingMemento fPreviousEncodingMemento; + + private Reader fReader; + + private Reader fResettableReader; + private byte[] UTF16BEBOM = new byte[]{(byte) 0xFE, (byte) 0xFF}; + + private byte[] UTF16LEBOM = new byte[]{(byte) 0xFF, (byte) 0xFE}; + private byte[] UTF3BYTEBOM = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}; + + public CodedStreamCreator() { + super(); + } + + public CodedStreamCreator(String filename, char[] characterArray) { + super(); + fFilename = filename; + fReader = new CharArrayReader(characterArray); + } + + public CodedStreamCreator(String filename, Reader reader) { + super(); + fFilename = filename; + fReader = reader; + } + + public CodedStreamCreator(String filename, String textString) { + super(); + fFilename = filename; + fReader = new StringReader(textString); + } + + /** + * The primary method which contains the highest level rules for how to + * decide appropriate decoding rules: 1. first check for unicode stream 2. + * then looked for encoding specified in content (according to the type of + * content that is it ... xml, html, jsp, etc. 3. then check for various + * settings: file settings first, if null check project settings, if null, + * check user preferences. 4. lastly (or, what is the last user + * preference) is to use "workbench defaults". + */ + private void analyze() throws CoreException, IOException { + Reader resettableReader = getResettableReader(); + try { + if (fCurrentEncodingMemento == null) { + resettableReader.reset(); + fCurrentEncodingMemento = checkForEncodingInContents(); + } + // if encoding stratagy doesn't provide answer, + // then try file settings, project settings, + // user preferences, and + // finally workbench default. + // + if (fCurrentEncodingMemento == null || fCurrentEncodingMemento.getDetectedCharsetName() == null) { + resettableReader.reset(); + fCurrentEncodingMemento = getEncodingMementoFromResourceAndPreference(); + } + + // use DefaultNameRules from NonContentBasedEncodingRules as the + // final default + if (fEncodingMemento == null) { + handleNotProvidedFromContentCase(); + } + + fHasBeenAnalyzed = true; + } finally { + if (resettableReader != null) { + resettableReader.reset(); + } + } + } + + /** + * Need to check conversion early on. There's some danger than old + * contents of a file are set to empty, if an exception occurs. + * + * @param allText + * @param encoding + * @param encodingRule + * @throws java.io.UnsupportedEncodingException + * @throws MalformedOutputExceptionWithDetail + * @deprecated - we need to find "cheaper" way to to this functionality so + * likely to go away in future + */ + private void checkConversion(EncodingMemento memento, EncodingRule encodingRule) throws IOException { + String javaEncoding = memento.getJavaCharsetName(); + String detectedEncoding = memento.getDetectedCharsetName(); + Charset charset = Charset.forName(javaEncoding); + CharsetEncoder charsetEncoder = charset.newEncoder(); + charsetEncoder.onMalformedInput(CodingErrorAction.REPORT); + charsetEncoder.onUnmappableCharacter(CodingErrorAction.REPORT); + Reader reader = getResettableReader(); + reader.reset(); + int currentChar = reader.read(); + int currentPos = 1; + try { + while (currentChar != -1) { + // note: this can probably be made more + // efficient later to + // check buffer by buffer, instead of + // character by character. + try { + boolean canConvert = charsetEncoder.canEncode((char) currentChar); + if (!canConvert) { + if (encodingRule == EncodingRule.IGNORE_CONVERSION_ERROR) { + // if we're told to ignore the + // encoding conversion + // error, + // notice we still want to detect + // and log it. We simply + // don't throw the exception, and + // we do continue with + // the + // save. + Logger.log(Logger.ERROR, "Encoding Conversion Error during save"); //$NON-NLS-1$ + } else { + throw new MalformedOutputExceptionWithDetail(javaEncoding, detectedEncoding, currentPos); + } + } + currentChar = reader.read(); + currentPos++; + } + // IBM's JRE seems to throw NPE when DBCS char is given to + // SBCS charsetEncoder + catch (NullPointerException e) { + throw new CharConversionErrorWithDetail(javaEncoding); //$NON-NLS-1$ + } + } + // if we get all the way through loop without throwing exception, + // then there must + // be an error not detectable when going character by character. + throw new CharConversionErrorWithDetail(javaEncoding); //$NON-NLS-1$ + } finally { + reader.reset(); + } + } + + private EncodingMemento checkForEncodingInContents() throws CoreException, IOException { + EncodingMemento result = null; + + // if encoding memento already set, and no need to get again. + if (fEncodingMemento != null) { + result = fEncodingMemento; + } else { + if (fClientSuppliedReader) { + fReader.reset(); + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); + try { + IContentDescription contentDescription = contentTypeManager.getDescriptionFor(fReader, fFilename, IContentDescription.ALL); + if (contentDescription != null) { + fEncodingMemento = createMemento(contentDescription); + } else { + fEncodingMemento = CodedIO.createEncodingMemento("UTF-8"); //$NON-NLS-1$ + } + } catch (NullPointerException e) { + // TODO: work around for 5/14 bug in base, should be + // removed when move up to 5/21 + // just created a simple default one + fEncodingMemento = CodedIO.createEncodingMemento("UTF-8"); //$NON-NLS-1$ + } + result = fEncodingMemento; + } else { + throw new IllegalStateException("unexpected state: encodingMemento was null but no input stream supplied"); //$NON-NLS-1$ + } + } + // try { + // result = getEncodingDetector().getEncodingMemento(); + // if (result != null && !result.isValid() && !forceDefault()) { + // throw new UnsupportedCharsetExceptionWithDetail(result); + // } + // } + // finally { + // handleStreamClose(fEncodingDetectorStream); + // } + return result; + } + + + private void dump(OutputStream outputStream, EncodingRule encodingRule, boolean use3ByteBOMifUTF8) throws CoreException, IOException { + getCurrentEncodingMemento(); + String javaEncodingName = null; + if (encodingRule == EncodingRule.CONTENT_BASED) { + if (fCurrentEncodingMemento.isValid()) { + javaEncodingName = fCurrentEncodingMemento.getJavaCharsetName(); + } else { + throw new UnsupportedCharsetExceptionWithDetail(fCurrentEncodingMemento); + } + } else if (encodingRule == EncodingRule.IGNORE_CONVERSION_ERROR) + javaEncodingName = fCurrentEncodingMemento.getJavaCharsetName(); + else if (encodingRule == EncodingRule.FORCE_DEFAULT) + javaEncodingName = fCurrentEncodingMemento.getAppropriateDefault(); + // write appropriate "header" unicode BOM bytes + // Note: Java seems to write appropriate header for + // UTF-16, but not + // UTF-8 nor UTF-16BE. This + // may vary by JRE version, so need to test well. + // Note: javaEncodingName can be null in invalid + // cases, so we no hard + // to skip whole check if that's the case. + if (javaEncodingName != null) { + if ((javaEncodingName.equals(UTF_8_CHARSET_NAME) && use3ByteBOMifUTF8) || (javaEncodingName.equals(UTF_8_CHARSET_NAME) && fCurrentEncodingMemento.isUTF83ByteBOMUsed())) { + outputStream.write(UTF3BYTEBOM); + } else if (javaEncodingName.equals(UTF_16LE_CHARSET_NAME)) { + outputStream.write(UTF16LEBOM); + } else if (javaEncodingName.equals(UTF_16BE_CHARSET_NAME)) { + outputStream.write(UTF16BEBOM); + } + } + // TODO add back in line delimiter handling the + // "right" way (updating + // markers, not requiring string, etc. .. may need + // to move to document + // level) + //allTextBuffer = + // handleLineDelimiter(allTextBuffer, document); + Reader reader = getResettableReader(); + // be sure to test large "readers" ... we'll need + // to make sure they all + // can reset to initial position (StringReader, + // CharArrayReader, and + // DocumentReader should all work ok). + reader.reset(); + // There must be cleaner logic somehow, but the + // idea is that + // javaEncodingName can be null + // if original detected encoding is not valid (and + // if FORCE_DEFAULT was + // not specified). Hence, we WANT the first + // Charset.forName to + // throw appropriate exception. + Charset charset = null; + + // this call checks "override" properties file + javaEncodingName = CodedIO.getAppropriateJavaCharset(javaEncodingName); + + if (javaEncodingName == null) { + charset = Charset.forName(fCurrentEncodingMemento.getDetectedCharsetName()); + } else { + charset = Charset.forName(javaEncodingName); + } + CharsetEncoder charsetEncoder = charset.newEncoder(); + if (!(encodingRule == EncodingRule.IGNORE_CONVERSION_ERROR)) { + charsetEncoder.onMalformedInput(CodingErrorAction.REPORT); + charsetEncoder.onUnmappableCharacter(CodingErrorAction.REPORT); + } else { + charsetEncoder.onMalformedInput(CodingErrorAction.REPLACE); + charsetEncoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + + } + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, charsetEncoder); + //TODO: this may no longer be needed (and is at + // least wrong spot for + // it). + // if (checkConversion && (!(encodingRule == + // EncodingRule.IGNORE_CONVERSION_ERROR))) { + // checkConversion(fCurrentEncodingMemento, + // encodingRule); + // } + char[] charbuf = new char[CodedIO.MAX_BUF_SIZE]; + int nRead = 0; + try { + while (nRead != -1) { + nRead = reader.read(charbuf, 0, MAX_BUF_SIZE); + if (nRead > 0) { + outputStreamWriter.flush(); + outputStreamWriter.write(charbuf, 0, nRead); + } + } + } catch (UnmappableCharacterException e) { + checkConversion(fCurrentEncodingMemento, encodingRule); + } finally { + // since we don't own the original output stream, we + // won't close it ours. + // the caller who passed it to us must close original one + // when appropriate. + // (but we do flush to be sure all up-to-date) + outputStreamWriter.flush(); + } + } + + private boolean get3ByteBOMPreference() { + return SSECorePlugin.getDefault().getPluginPreferences().getBoolean(CommonEncodingPreferenceNames.USE_3BYTE_BOM_WITH_UTF8); + } + + public ByteArrayOutputStream getCodedByteArrayOutputStream() throws CoreException, IOException { + return getCodedByteArrayOutputStream(EncodingRule.CONTENT_BASED); + } + + public ByteArrayOutputStream getCodedByteArrayOutputStream(EncodingRule encodingRule) throws CoreException, IOException { + //Assert.isNotNull(fPreviousEncodingMemento, + // "previousEncodingMemento + // needs to be set first"); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE); + dump(byteArrayOutputStream, encodingRule, get3ByteBOMPreference()); + return byteArrayOutputStream; + } + + public EncodingMemento getCurrentEncodingMemento() throws CoreException, IOException { + //Assert.isNotNull(fPreviousEncodingMemento, + // "previousEncodingMemento + // needs to be set first"); + if (!fHasBeenAnalyzed) { + analyze(); + } + // post condition + Assert.isNotNull(fCurrentEncodingMemento, "illegal post condition state"); //$NON-NLS-1$ + // be sure to carry over appropriate encoding + // "state" that may be + // relevent. + if (fPreviousEncodingMemento != null) { + fCurrentEncodingMemento.setUTF83ByteBOMUsed(fPreviousEncodingMemento.isUTF83ByteBOMUsed()); + } + return fCurrentEncodingMemento; + } + + /* + * This method is called only when encoding is not detected in the file. + * + * Here is encoding lookup order we will try: - try resource content + * description (Eclipse Text file encoding) - try resource content + * properties (for JSP only) - try content type encoding preferences (for + * HTML only) - try resource content description (Eclipse Text file + * encoding, implicit check) + * + * Note: This method appears in both CodedReaderCreator and + * CodedStreamCreator (with just a minor difference). They should be kept + * the same. + */ + private EncodingMemento getEncodingMementoFromResourceAndPreference() throws IOException, CoreException { + EncodingMemento encodingMemento = fEncodingMemento; + + // Follow Eclipse Platform's direction. Get the charset from IFile. + if (fIFile != null) { + String charset = fIFile.getCharset(); + encodingMemento = CodedIO.createEncodingMemento(charset); + } + + return encodingMemento; + } + + private Reader getResettableReader() { + if (fResettableReader == null) { + if (fReader.markSupported()) { + fResettableReader = fReader; + } else { + fResettableReader = new BufferedReader(fReader); + try { + fResettableReader.mark(MAX_MARK_SIZE); + } catch (IOException e) { + // impossible, since we just checked if + // markable + throw new Error(e); + } + + } + } + return fResettableReader; + } + + protected void handleNotProvidedFromContentCase() { + // move to "detectors" if not already + String specDefault = null; + //specDefault = getEncodingDetector().getSpecDefaultEncoding(); + String charset = NonContentBasedEncodingRules.useDefaultNameRules(specDefault); + Assert.isNotNull(charset, PROGRAM_ERROR__FAILED_TO_FIND_ANY_CHARSET_ANYWHERE_); + fCurrentEncodingMemento = CodedIO.createEncodingMemento(charset); + } + + // TODO We just copy the content properties encoding to current resource's + // encoding for now. May improve the UI later by setting an informational + // message and/or disable the content properties encoding field. + // TODO make priviate if needed, else remove + void migrateContentPropertiesEncoding(String encoding) throws CoreException { + if (fIFile != null) + fIFile.setCharset(encoding, null); + final IFile file = fIFile; + final String charset = encoding; + // TODO: externalize string later + Job migrater = new Job(SSECoreMessages.Migrate_Charset) { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (file != null) { + try { + file.setCharset(charset, null); + } catch (CoreException e) { + Logger.logException(e); + } + } + return Status.OK_STATUS; + } + }; + migrater.setSystem(true); + migrater.schedule(); + + } + + /** + * + */ + private void resetAll() { + fFilename = null; + fReader = null; + fPreviousEncodingMemento = null; + fCurrentEncodingMemento = null; + fHasBeenAnalyzed = false; + fClientSuppliedReader = false; + } + + public void set(IFile file, Reader reader) { + fIFile = file; + set(file.getName(), reader); + } + + public void set(String filename, char[] characterArray) { + resetAll(); + fFilename = filename; + fReader = new CharArrayReader(characterArray); + } + + public void set(String filename, Reader reader) { + resetAll(); + fFilename = filename; + fReader = reader; + fClientSuppliedReader = true; + } + + public void set(String filename, String textString) { + set(filename, new StringReader(textString)); + } + + public void setPreviousEncodingMemento(EncodingMemento previousEncodingMemento) { + fPreviousEncodingMemento = previousEncodingMemento; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CommonCharsetNames.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CommonCharsetNames.java new file mode 100644 index 0000000..b563a28 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CommonCharsetNames.java
@@ -0,0 +1,290 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.encoding.util.Logger; +import org.osgi.framework.Bundle; + +/** + * CommonCharsets is a utility class to provide a central place to map some + * IANA charset name to a Java charset name. In 1.4 JRE's this mostly is a + * thin wrapper of existing Charset functionality. It does, however, allow + * some "overriding" of the built in JRE mappings in the event they are + * necessary. See CodedIO.checkMappingOverrides. + * <p> + * This class also provides some convenient human readable description for the + * charset names which can be used in user interfaces. The description is NL + * aware based on locale. The data is populated via the charset.properties + * file only once, based on static initialization of the hashtables. + * <p> + * The IANA tags are based on reference information found at the + * http://www.iana.org site. Specifically see + * http://www.iana.org/assignments/character-sets + */ +public final class CommonCharsetNames { + + private static Properties defaultIANAmappings = null; + + private static ArrayList encodings = null; + + private static Hashtable supportedEncodingDisplayNames = null; + + private static Map prefNames = null; + + /** + * Returns list of commonly available encoding names. Suitable for + * populating a UI dialog or drop down. This list would be a subset of all + * possible charsets the VM supports (which can get into the hundreds). + * For the VM supported charsets, use + * <code>Charset.availableCharsets()</code> + * + * @return String[] + */ + public static String[] getCommonCharsetNames() { + String[] enc = new String[getEncodings().size()]; + getEncodings().toArray(enc); + return enc; + } + + /** + * @return Returns the defaultIANAmappings. + */ + private static Properties getDefaultIANAMappings() { + if (defaultIANAmappings == null) { + defaultIANAmappings = new Properties(); + Bundle keyBundle = Platform.getBundle(ICodedResourcePlugin.ID); + IPath keyPath = new Path("config/defaultIANA.properties"); //$NON-NLS-1$ + URL location = Platform.find(keyBundle, keyPath); + InputStream propertiesInputStream = null; + try { + propertiesInputStream = location.openStream(); + defaultIANAmappings.load(propertiesInputStream); + } + catch (IOException e) { + // if can't read, just assume there's no + // default IANA mappings + // and repeated attempts will not occur, + // since they + // will be represented by an empty + // Properties object + } + } + return defaultIANAmappings; + } + + /** + * Returns display (translated) string for encoding name. If there is no + * "custom" translated version available, it defers to ther VM's Charset + * support. It will return null if no display name is available. + * + * @param String + * charset name + * @return Human friendly display name + */ + public static String getDisplayString(String charsetName) { + if (charsetName == null) + return null; + String result = (String) getSupportedEncodingDisplayNames().get(charsetName); + if (result == null) { + // if we don't have a special one, just return + // what's provided by Charset + + try { + Charset charset = Charset.forName(charsetName); + result = charset.displayName(); + } + catch (UnsupportedCharsetException e) { + // if not supported, the display name is + // the least of clients concerns :) + } + } + return result; + } + + public static String getPreferenceName(String charsetName) { + if (charsetName == null) + return null; + + String result = (String) getPreferenceNames().get(charsetName); + if (result == null) { + return charsetName; + } + + return result; + } + /** + * @return Returns the javaEncodings. + */ + private static ArrayList getEncodings() { + if (encodings == null) { + initHashTables(); + } + return encodings; + } + + public static String getIanaPreferredCharsetName(String charsetName) { + String preferredName = charsetName; + + try { + Charset charset = Charset.forName(charsetName); + if (charset.name() != null) { + preferredName = charset.name(); + } + } + catch (IllegalCharsetNameException e) { + // just return input if illegal + } + catch (UnsupportedCharsetException e) { + // just return input if illegal + } + return preferredName; + } + + /** + * Returns a default IANA name that is listed in CommonCharsetNames. Here + * is how it checks: 1. check to see if charsetName is in the + * CommonCharsetNames list and if so, just return it. 2. check to see if + * charsetName is listed in defaultIANAmappings which contains a mapping + * of more common encodings and the default IANA name they should map to. + * 3. return defaultIanaName if all else fails + */ + public static String getPreferredDefaultIanaName(String charsetName, String defaultIanaName) { + String preferredName = defaultIanaName; + String guessedName = charsetName; + try { + guessedName = CodedIO.getAppropriateJavaCharset(charsetName); + } + catch (IllegalCharsetNameException e) { + // just ignore if illegal + } + catch (UnsupportedCharsetException e) { + // just ignore if illegal + } + if (getEncodings().contains(guessedName)) + preferredName = guessedName; + else { + preferredName = getDefaultIANAMappings().getProperty(guessedName, preferredName); + } + + return preferredName; + } + + /** + * @return + */ + private static Hashtable getSupportedEncodingDisplayNames() { + if (supportedEncodingDisplayNames == null) { + initHashTables(); + } + return supportedEncodingDisplayNames; + } + + private static Map getPreferenceNames() { + if (prefNames == null) { + initHashTables(); + } + return prefNames; + } + + private static void initHashTables() { + if (supportedEncodingDisplayNames == null) { + // Initialize hash table for encoding table + supportedEncodingDisplayNames = new Hashtable(); + encodings = new ArrayList(); + prefNames = new HashMap(); + + ResourceBundle bundle = null; + InputStream bundleStream = null; + try { + URL bundleURL = Platform.find(Platform.getBundle(ICodedResourcePlugin.ID), Path.fromOSString("$nl$/config/charset.properties")); //$NON-NLS-1$ + if (bundleURL != null) { + bundleStream = bundleURL.openStream(); + bundle = new PropertyResourceBundle(bundleStream); + } + + String totalNumString = bundle.getString("totalnumber");//$NON-NLS-1$ + int totalNum = 0; + if (totalNumString.length() != 0) { + try { + totalNum = Integer.valueOf(totalNumString).intValue(); + } + catch (NumberFormatException e) { + totalNum = 0; + } + } + + for (int i = 0; i < totalNum; i++) { + String iana = bundle.getString("codeset." + i + ".iana");//$NON-NLS-2$//$NON-NLS-1$ + String displayName = bundle.getString("codeset." + i + ".label");//$NON-NLS-2$//$NON-NLS-1$ + String prefName = null; + try { + prefName = bundle.getString("codeset." + i + ".prefName"); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (MissingResourceException e) { + } + + encodings.add(iana); + supportedEncodingDisplayNames.put(iana, displayName); + if (prefName != null) + prefNames.put(prefName, iana); + } + } + catch (IOException e) { + Logger.logException("invalid install or configuration", e); //$NON-NLS-1$ + } + finally { + try { + if (bundleStream != null) + bundleStream.close(); + } + catch (IOException x) { + } + } + } + } + + public static void main(String[] args) { + // unit test only + String test = "Cp1252"; //$NON-NLS-1$ + String result = CommonCharsetNames.getIanaPreferredCharsetName(test); + System.out.println(test + " --> " + result); //$NON-NLS-1$ + + test = "MS932"; //$NON-NLS-1$ + result = CommonCharsetNames.getIanaPreferredCharsetName(test); + System.out.println(test + " --> " + result); //$NON-NLS-1$ + + } + + public CommonCharsetNames() { + super(); + initHashTables(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CommonEncodingPreferenceNames.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CommonEncodingPreferenceNames.java new file mode 100644 index 0000000..087e03a --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/CommonEncodingPreferenceNames.java
@@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +/** + * Common preference keys used to specify encoding and end of line. + */ +public class CommonEncodingPreferenceNames { + + private CommonEncodingPreferenceNames() { + // empty private constructor so users cannot instantiate class + } + + /** + * Constant to be used when referring to CR/MAC line delimiter + * @deprecated - no longer used + */ + public static final String CR = "EOL_Mac"; //$NON-NLS-1$ + /** + * Constant to be used when referring to CRLF/WINDOWS line delimiter + * @deprecated - no longer used + */ + public static final String CRLF = "EOL_Windows"; //$NON-NLS-1$ + /** + * The end-of-line character(s) to use. + * @deprecated - no longer used + */ + public static final String END_OF_LINE_CODE = "endOfLineCode";//$NON-NLS-1$ + /** + * The character code to use when reading a file. + */ + public static final String INPUT_CODESET = "inputCodeset";//$NON-NLS-1$ + + /** + * Constant to be used when referring to LF/UNIX line delimiter + * @deprecated - no longer used + */ + public static final String LF = "EOL_Unix"; //$NON-NLS-1$ + /** + * Constant to be used when referring to No translation of line delimiters + * @deprecated - no longer used + */ + public static final String NO_TRANSLATION = ""; //$NON-NLS-1$ + /** + * The character code to use when writing a file. + */ + public static final String OUTPUT_CODESET = "outputCodeset";//$NON-NLS-1$ + + /** + * String representation of CR/MAC line delimiter + * @deprecated - no longer used + */ + public static final String STRING_CR = "\r";//$NON-NLS-1$ + + /** + * String representation of CRLF/WINDOWS line delimiter + * @deprecated - no longer used + */ + public static final String STRING_CRLF = "\r\n";//$NON-NLS-1$ + + /** + * String representation of LF/UNIX line delimiter + * @deprecated - no longer used + */ + public static final String STRING_LF = "\n";//$NON-NLS-1$ + + /** + * String Use 3 byte BOM (Byte Order Mark) when saving UTF-8 encoded files + */ + public static final String USE_3BYTE_BOM_WITH_UTF8 = "Use3ByteBOMWithUTF8"; //$NON-NLS-1$ +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ContentBasedPreferenceGateway.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ContentBasedPreferenceGateway.java new file mode 100644 index 0000000..cd28db3 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ContentBasedPreferenceGateway.java
@@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.osgi.framework.Bundle; +import org.osgi.service.prefs.Preferences; + + +public class ContentBasedPreferenceGateway { + private static String DEFAULT_LOCATION = "org.eclipse.wst.sse.core"; //$NON-NLS-1$ + private static String RUNTIME_XML_ID = "org.eclipse.core.runtime.xml"; //$NON-NLS-1$ + private static String SSE_XML_ID = "org.eclipse.wst.xml.core.xmlsource"; //$NON-NLS-1$ + + /** + * @param pluginId + * @return + */ + private static boolean bundleExists(String pluginId) { + + // this just verifies there's really a plugin with this ID in "stack" + Bundle bundle = Platform.getBundle(pluginId); + return (!(bundle == null)); + } + + /** + * @param contentType + * @return + */ + private static String getContributorPluginId(IContentType contentType) { + // TODO: need to have registration info here, but for now, we'll use + // simple heuristic to cover the cases we know about. + String fullId = null; + if (contentType == null) { + fullId = DEFAULT_LOCATION; + } else { + fullId = contentType.getId(); + } + // only one known case, so far, of hard coded re-direction + // (not sure this is even needed, but just in case). + // We don't want to store/change runtime.xml preferences + if (RUNTIME_XML_ID.equals(fullId)) { + fullId = SSE_XML_ID; + } + String pluginId = inferPluginId(fullId); + return pluginId; + } + + private static Preferences getDefaultPreferences(IContentType contentType) { + IEclipsePreferences eclipsePreferences = Platform.getPreferencesService().getRootNode(); + // TODO: eventaully need extension mechanism to avoid these hard coded + // mechanism. + // The idea is to load/store based on plugin's preferences, where the + // content type was contributed + // Eventually, too, we could do more "dynamic lookup" to get parent + // types for defaults, etc. + + // Get default plugin preferences + String pluginPreferenceLocation = DefaultScope.SCOPE + IPath.SEPARATOR + getContributorPluginId(contentType); + Preferences pluginPreferences = eclipsePreferences.node(pluginPreferenceLocation); + + // the below code does not work at this time because content type + // preferences are stored in the place as plugin preferences + + // Preferences contentPreferences = null; + // if (contentType != null) { + // contentPreferences = pluginPreferences.node(contentType.getId()); + // } + // else { + // contentPreferences = pluginPreferences.node(DEFAULT_LOCATION ); + // } + // + // return contentPreferences; + + return pluginPreferences; + + } + + private static Preferences getDefaultPreferences(String contentTypeId) { + IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeId); + return getDefaultPreferences(contentType); + } + + public static Preferences getPreferences(IContentType contentType) { + IEclipsePreferences eclipsePreferences = Platform.getPreferencesService().getRootNode(); + // TODO: eventaully need extension mechanism to avoid these hard coded + // mechanism. + // The idea is to load/store based on plugin's preferences, where the + // content type was contributed + // Eventually, too, we could do more "dynamic lookup" to get parent + // types for defaults, etc. + + // Get instance plugin preferences + String pluginPreferenceLocation = Plugin.PLUGIN_PREFERENCE_SCOPE + IPath.SEPARATOR + getContributorPluginId(contentType); + Preferences pluginPreferences = eclipsePreferences.node(pluginPreferenceLocation); + + // the below code does not work at this time because content type + // preferences are stored in the place as plugin preferences + + // Preferences contentPreferences = null; + // if (contentType != null) { + // contentPreferences = pluginPreferences.node(contentType.getId()); + // } + // else { + // contentPreferences = pluginPreferences.node(DEFAULT_LOCATION ); + // } + // + // return contentPreferences; + return pluginPreferences; + + } + + public static Preferences getPreferences(String contentTypeId) { + IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeId); + return getPreferences(contentType); + } + + public static String getPreferencesString(IContentType contentType, String key) { + Preferences preferences = getPreferences(contentType); + String value = preferences.get(key, getDefaultPreferences(contentType).get(key, null)); + return value; + } + + public static String getPreferencesString(String contentTypeId, String key) { + Preferences preferences = getPreferences(contentTypeId); + String value = preferences.get(key, getDefaultPreferences(contentTypeId).get(key, null)); + return value; + } + + /** + * @param fullId + * @return + */ + private static String inferPluginId(String fullId) { + // simply trim off last "segment" from full ID. + int lastSegmentPos = fullId.lastIndexOf('.'); + String pluginId = null; + if (lastSegmentPos != -1) { + pluginId = fullId.substring(0, lastSegmentPos); + } else { + // weird case? We'll at least put/get them somewhere + pluginId = DEFAULT_LOCATION; + } + if (!bundleExists(pluginId)) { + // use default location + pluginId = DEFAULT_LOCATION; + } + return pluginId; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ContentTypeEncodingPreferences.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ContentTypeEncodingPreferences.java new file mode 100644 index 0000000..4c82676 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ContentTypeEncodingPreferences.java
@@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.wst.sse.core.internal.encoding.util.Assert; + + +/** + * A convenience class to statically get preferenences. + */ + +public abstract class ContentTypeEncodingPreferences { + + // actually a null/empty string also means use workbench default so this + // constant might not really be necessary + public static final String WORKBENCH_DEFAULT = "WORKBENCH_DEFAULT"; //$NON-NLS-1$ + + + private static final String getJavaPlatformDefaultEncoding() { + // note: its important to use this system property, + // instead + // of + // ByteToCharConverter.getDefault().getCharacterEncoding() + // inorder to handle changes "on the fly". the + // ByteToCharConverter + // default is apparently set only when VM starts. + // There's not really any "cusomter scnererios" + // that change the + // default encoding "on the fly", but its at least + // used during + // our automated tests. + String enc = System.getProperty("file.encoding"); //$NON-NLS-1$ + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + return enc; + } + + + public static final String getPreferredNewLineDelimiter(String contentTypeId) { + String result = null; + String newLineCode = null; + newLineCode = ContentBasedPreferenceGateway.getPreferencesString(contentTypeId, CommonEncodingPreferenceNames.END_OF_LINE_CODE); + if (newLineCode == null) + result = null; + else if (newLineCode.equals(CommonEncodingPreferenceNames.CR)) + result = CommonEncodingPreferenceNames.STRING_CR; + else if (newLineCode.equals(CommonEncodingPreferenceNames.LF)) + result = CommonEncodingPreferenceNames.STRING_LF; + else if (newLineCode.equals(CommonEncodingPreferenceNames.CRLF)) + result = CommonEncodingPreferenceNames.STRING_CRLF; + return result; + } + + /** + * Returns current output encoding preference for contentTypeIdentifier + * (unique IANA encoding) + */ + public static final String getUserPreferredCharsetName(String contentTypeId) { + String prefEncoding = ContentBasedPreferenceGateway.getPreferencesString(contentTypeId, CommonEncodingPreferenceNames.OUTPUT_CODESET); + String encoding = prefEncoding; + // get workbench encoding preference if preference + // requests it + if (prefEncoding == null || prefEncoding.trim().length() == 0 || prefEncoding.equals(ContentTypeEncodingPreferences.WORKBENCH_DEFAULT)) { + encoding = ContentTypeEncodingPreferences.getWorkbenchPreferredCharsetName(); + } + return encoding; + } + + /** + * Utility method to get specified preference. Subclasses can't override, + * since we expect this to work in a consistent way. Note: this is + * specific to HTML and CSS and is intended to supply a "default spec" + * other than the workbench platform's default, only for those cases where + * there is no encoding specified anywhere else, e.g. in the file, or as a + * file or folder property. + */ + public static final String getUserSpecifiedDefaultEncodingPreference() { + String ContentTypeID_HTML = "org.eclipse.wst.html.core.htmlsource"; //$NON-NLS-1$ + return getUserSpecifiedDefaultEncodingPreference(ContentTypeID_HTML); + } + + public static final String getUserSpecifiedDefaultEncodingPreference(String contentTypeID) { + String enc = null; + + // first try to get base's default encoding for content type + IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeID); + if (contentType != null) { + enc = contentType.getDefaultCharset(); + } + + // next try to get sse's default encoding for content type + if (enc == null || enc.trim().length() == 0) { + enc = ContentBasedPreferenceGateway.getPreferencesString(contentTypeID, CommonEncodingPreferenceNames.INPUT_CODESET); + } + + // next, just try and use workbench encoding + if (enc == null || enc.trim().length() == 0) { + enc = getWorkbenchSpecifiedDefaultEncoding(); + } + + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + return enc; + } + + /** + * Returns Workbench encoding preference. Note: if workbench encoding is + * null, platform encoding will be returned. + */ + private static final String getWorkbenchPreferredCharsetName() { + String charset = ResourcesPlugin.getEncoding(); + charset = CommonCharsetNames.getIanaPreferredCharsetName(charset); + return charset; + } + + /** + * Returns Workbench encoding preference. Will return null if none + * specified. + */ + private static final String getWorkbenchSpecifiedDefaultEncoding() { + ResourcesPlugin resourcePlugin = ResourcesPlugin.getPlugin(); + String enc = resourcePlugin.getPluginPreferences().getString(ResourcesPlugin.PREF_ENCODING); + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + return enc; + } + + public static final String useDefaultNameRules(IResourceCharsetDetector encodingProvider) { + String result = null; + String enc = null; + enc = encodingProvider.getSpecDefaultEncoding(); + if (enc != null) { + result = enc; + } else { + enc = getUserSpecifiedDefaultEncodingPreference(); + if (enc != null && enc.trim().length() > 0) { + result = enc; + } else { + if (enc == null || enc.trim().length() == 0) { + enc = getWorkbenchSpecifiedDefaultEncoding(); + if (enc != null) { + result = enc; + } + } + if (enc == null || enc.trim().length() == 0) { + enc = getJavaPlatformDefaultEncoding(); + // enc should never be null (but we'll + // check anyway) + if (enc != null) { + result = enc; + } + } + } + } + Assert.isNotNull(enc, "post condition invalid"); //$NON-NLS-1$ + result = CodedIO.checkMappingOverrides(enc); + return result; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/EncodingMemento.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/EncodingMemento.java new file mode 100644 index 0000000..c195a84 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/EncodingMemento.java
@@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import org.eclipse.core.runtime.content.IContentDescription; + + +/** + * This class is to simply hold information and data about the type of + * encoding found for a resource. It not only includes names, etc., but also + * gives hints about the algorithm, or rule, that the encodng was determined. + * Having all this info in a central object, associated with the Document + * (technically, IStructuredDocument), allows for better user error messages, + * and better handling of knowing how to dump a file, given we know how it was + * loaded. + * + * Note: the data in this class is only valid if its has actually gone through + * the loading or dumping sequence. It is not accurate, for example, if a + * structuredDocument is simply created and then setText called. In this type + * of case, accuracy for loading and dumping is not required, since its all + * re-discovered. One limitation is that structuredDocument's created "from + * scratch" this way, don't have any encoding information to count on, and + * would have to arrange the processing to be done. (And it is done, + * automatically if going through loader or dumper, but perhaps not in future + * new uses. TODO: this can be inproved in future versions.) + * + * isInitialized is set when the loader or dumper processes have been used, + * but even this can't be counted on 100% if the document has been modified + * since. + * + */ +public class EncodingMemento implements Cloneable { + + public final static String CLONED = "cloned"; //$NON-NLS-1$ + public final static String DEFAULTS_ASSUMED_FOR_EMPTY_INPUT = "DefaultsAssumedForEmptyInput"; //$NON-NLS-1$ + public final static String DEFAULTS_USED_DUE_TO_SMALL_STREAM = "defaultsUsedDueToSmallStream"; //$NON-NLS-1$ + + + /* + * Strings to be used for tracing. TODO: need to clean this up, we no + * longer use all of them + */ + public final static String DETECTED_STANDARD_UNICODE_BYTES = "detectedStandardUnicodeBytes"; //$NON-NLS-1$ + public final static String FOUND_ENCODING_IN_CONTENT = "foundEncodingInContent"; //$NON-NLS-1$ + public final static String FOUND_ENCODING_IN_STREAM = "foundEncodingInStream"; //$NON-NLS-1$ + public final static String FOUND_ENCODING_IN_STRUCTURED_DOCUMENT = "foundEncodingInStructuredDocument"; //$NON-NLS-1$ + public final static String GUESSED_ENCODING_FROM_STREAM = "GuessEncodingFromStream"; //$NON-NLS-1$ + public final static String JAVA_NAME_FOUND_AS_IANA_NAME = "noMappingFoundButJavaNameFoundToBeIANAName"; //$NON-NLS-1$ + public final static String JAVA_NAME_FOUND_IN_ALIAS_NAME = "noMappingFoundButJavaNameFoundInAliasTable"; //$NON-NLS-1$ + public final static String NO_IANA_NAME_FOUND = "noMappingFoundFromJavaNameToIANAName"; //$NON-NLS-1$ + public final static String USED_CONTENT_TYPE_DEFAULT = "UsedContentTypeDefault"; //$NON-NLS-1$ + public final static String USED_JAVA_DEFAULT = "UsedJavaDefault"; //$NON-NLS-1$ + public final static String USED_MEMENTO_FROM_LOAD = "usedMementoFromLoad"; //$NON-NLS-1$ + public final static String USED_PROPERTY_SETTINGS = "USED_PROPERTY_SETTINGS"; //$NON-NLS-1$ + public final static String USED_USER_SPECIFIED_PREFERENCE = "UsedUserSpecifiedPreference"; //$NON-NLS-1$ + public final static String USED_WORKSPACE_DEFAULT = "UsedWorkspaceDefault"; //$NON-NLS-1$ + public final static String USER_IS_USING_JAVA_ENCODING = "UserIsUsingJavaEncoding"; //$NON-NLS-1$ + private String fAppropriateDefault; + private String fDetectedCharsetName; + private String fInvalidEncoding; + + + private String fJavaCharsetName; + private boolean fUnicodeStream; + private boolean fUTF83ByteBOMUsed; + + private byte[] fBOM; + + public EncodingMemento() { + super(); + } + + /** + * Returns a clone of this object. + */ + public Object clone() { + EncodingMemento object = null; + try { + object = (EncodingMemento) super.clone(); + } + catch (CloneNotSupportedException e) { + // impossible, since we're implementing here + } + + return object; + + } + + /** + * Returns the appropriateDefault. This is only set if an invalid encoding + * was found, and contains an charset appropriate to use as a default + * value, if, for example, the user decides to load the document anyway, + * even though the charset was found to be invalid. + * + * @return String + */ + public String getAppropriateDefault() { + if (fAppropriateDefault == null) { + fAppropriateDefault = NonContentBasedEncodingRules.useDefaultNameRules(null); + } + return fAppropriateDefault; + } + + /** + * Returns the charset name, if it is different from the charset name + * found in getJavaCharsetName. This can happen, for example, if there are + * differences in case. This method might return SHIFT_JIS, and the the + * getJavaCharsetName might return Shift_JIS -- if SHIFT_JIS was detected + * in file/document. If the original file contained the correct case, then + * this method would return null. The getJavaCharsetName is typically the + * one that should always be used, and this one only used for certain + * error conditions, or or if when creating a "duplicate" resource, it was + * desired to use exactly the charset name as in the original document. As + * an example of this later case, the original document might contain + * ISO-8859-9, but the detected charset name might contain ISO-8859-9-I. + * + * @return String + */ + public String getDetectedCharsetName() { + return fDetectedCharsetName; + } + + /** + * Returns a charset name that was detected, but not found to be a charset + * suppoorted by the VM. + * + * @return String + */ + public String getInvalidEncoding() { + return fInvalidEncoding; + } + + /** + * Returns the java cononical charset name. + * + * @return String + */ + public String getJavaCharsetName() { + return fJavaCharsetName; + } + + /** + * Note: we may be able to remove this method, if it turns out this work + * is done by "text" type. + * + * @deprecated - + */ + public byte[] getUnicodeBOM() { + byte[] bom = null; + if (isUTF83ByteBOMUsed()) + bom = IContentDescription.BOM_UTF_8; + else if (isUnicodeStream()) + bom = fBOM; + return bom; + } + + /** + * Note: in our implementation, the stream is a unicode stream if the + * charset is UTF-16, UTF-16LE, or UTF-16BE. A stream with 3 byte BOM is + * not considered unicode stream here. + * + * @return returns true if is a unicode (UTF-16) stream + */ + public boolean isUnicodeStream() { + return fUnicodeStream; + } + + /** + * Note: in our implementation, the stream is a unicode stream if the + * charset is UTF-16, UTF-16LE, or UTF-16BE. A stream with 3 byte BOM is + * not considered unicode stream here. + * + * Set during load, can be used by dumper to write 3 byte BOM, which Java + * does not normally do. This helps maintain compatibility with other + * programs (those that wrote the 3 byte BOM there to begin with. + * + * @return boolean + */ + public boolean isUTF83ByteBOMUsed() { + return fUTF83ByteBOMUsed; + } + + public boolean isValid() { + return getInvalidEncoding() == null; + } + + /** + * Sets the appropriateDefault. + * + * @param appropriateDefault + * The appropriateDefault to set + */ + public void setAppropriateDefault(String appropriateDefault) { + fAppropriateDefault = appropriateDefault; + } + + + public void setDetectedCharsetName(String detectedCharsetName) { + fDetectedCharsetName = detectedCharsetName; + } + + public void setInvalidEncoding(String invalidEncoding) { + fInvalidEncoding = invalidEncoding; + } + + /** + * Sets the javaEncodingName. + * + * @param javaEncodingName + * The javaEncodingName to set + */ + public void setJavaCharsetName(String javaCharsetName) { + fJavaCharsetName = javaCharsetName; + } + + /** + * @param b + */ + public void setUnicodeStream(boolean unicodeStream) { + fUnicodeStream = unicodeStream; + + } + + /** + * Sets the uTF83ByteBOMfound. + * + * @param uTF83ByteBOMfound + * The uTF83ByteBOMfound to set + */ + public void setUTF83ByteBOMUsed(boolean uTF83ByteBOMUsed) { + fUTF83ByteBOMUsed = uTF83ByteBOMUsed; + } + + public void setUnicodeBOM(byte[] bom) { + fBOM = bom; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/EncodingRule.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/EncodingRule.java new file mode 100644 index 0000000..2d2988f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/EncodingRule.java
@@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + + +/** + * Class to provided "enumerated types" for encoding rule parameter. This is + * to be used by client to have some control over how encoding is determined. + */ +public class EncodingRule { + /** + * CONTENT_BASED means the class which uses the parameter (such as + * contentType Loaders) should use what ever rules it normally would. + * (Note, some content type loaders may not always literally use the file + * content to determine encoding, but the point is they should use what + * ever rules they normally would.) + */ + public static final EncodingRule CONTENT_BASED = new EncodingRule("CONTENT_BASED"); //$NON-NLS-1$ + /** + * FORCE_DEFAULT means the class which uses the parameter (such as + * contentType Loaders) should use what ever it defines as the default + * encoding. + */ + public static final EncodingRule FORCE_DEFAULT = new EncodingRule("FORCE_DEFAULT"); //$NON-NLS-1$ + + /** + * IGNORE_CONVERSION_ERROR means that the save operation should save even + * if it encounters conversion errors. This will result in some data loss, + * so should only be used after the user confirms that is indeed what they + * want to do. + */ + public static final EncodingRule IGNORE_CONVERSION_ERROR = new EncodingRule("IGNORE_CONVERSION_ERROR"); //$NON-NLS-1$ + + + private final String encodingRule; + + /** + * Constructor for EncodingRule is private, so no one can instantiate + * except this class itself. + */ + private EncodingRule(String ruleName) { + super(); + encodingRule = ruleName; + } + + public String toString() { + return encodingRule; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ICodedResourcePlugin.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ICodedResourcePlugin.java new file mode 100644 index 0000000..4b4900f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/ICodedResourcePlugin.java
@@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + + +public interface ICodedResourcePlugin { + String ID = "org.eclipse.wst.sse.core"; //$NON-NLS-1$ + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IContentDescriptionExtended.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IContentDescriptionExtended.java new file mode 100644 index 0000000..db2e0a4 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IContentDescriptionExtended.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import org.eclipse.core.runtime.QualifiedName; + + +public interface IContentDescriptionExtended { + /** + * The APPROPRIATE_DEFAULT field is used only when the + * IContentType.getDefaultCharset returns null. Its typically set from + * user preferences. Known uses cases are HTML and CSS, where there is no + * "spec default" for those content types. + */ + public static final QualifiedName APPROPRIATE_DEFAULT = new QualifiedName(ICodedResourcePlugin.ID, "appropriateDefault"); //$NON-NLS-1$ + /** + * The DETECTED_CHARSET property should be set when the "detected" charset + * is different from the java charset, even though functionally + * equivelent. This can occur, for example, when the cases are different, + * or when an alias name is used instead of the conanical name. + */ + public final static QualifiedName DETECTED_CHARSET = new QualifiedName(ICodedResourcePlugin.ID, "detectedCharset"); //$NON-NLS-1$ + /** + * The UNSUPPORTED_CHARSET property holds the charset value, if its been + * found to be an unsuppoted charset. This is helpful in error messages, + * or in cases when even though the charset is invalid, the java charset + * is assumed to be the default. + */ + public final static QualifiedName UNSUPPORTED_CHARSET = new QualifiedName(ICodedResourcePlugin.ID, "unsupportedCharset"); //$NON-NLS-1$ + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IResourceCharsetDetector.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IResourceCharsetDetector.java new file mode 100644 index 0000000..20dcbbf --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IResourceCharsetDetector.java
@@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; + +public interface IResourceCharsetDetector extends IStreamCharsetDetector { + void set(IStorage iStorage) throws CoreException; + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IStreamCharsetDetector.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IStreamCharsetDetector.java new file mode 100644 index 0000000..a2508f1 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/IStreamCharsetDetector.java
@@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +public interface IStreamCharsetDetector { + String getEncoding() throws IOException; + + String getSpecDefaultEncoding(); + + void set(InputStream inputStream); + + void set(Reader reader); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/NonContentBasedEncodingRules.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/NonContentBasedEncodingRules.java new file mode 100644 index 0000000..dd7158d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/NonContentBasedEncodingRules.java
@@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; + +public class NonContentBasedEncodingRules { + + private static final String getJavaPlatformDefaultEncoding() { + String enc = System.getProperty("file.encoding"); //$NON-NLS-1$ + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + return enc; + } + + + public static String getUserSpecifiedDefaultForContentType(IFile iFile) { + String enc = null; + + IContentType contentType = null; + try { + contentType = iFile.getContentDescription().getContentType(); + + // first try to get base's default encoding for content type + if (contentType != null) { + enc = contentType.getDefaultCharset(); + } + + // next try to get sse's default encoding for content type + if (enc == null || enc.trim().length() == 0) { + enc = ContentBasedPreferenceGateway.getPreferencesString(contentType, CommonEncodingPreferenceNames.INPUT_CODESET); + } + + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + } catch (CoreException e) { + // if core exception occurs, assume no preference! + enc = null; + } + return enc; + } + + public static String getUserSpecifiedDefaultForContentType(String contentTypeId) { + String enc = null; + + // first try to get base's default encoding for content type + IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeId); + if (contentType != null) { + enc = contentType.getDefaultCharset(); + } + + // next try to get sse's default encoding for content type + if (enc == null || enc.trim().length() == 0) { + enc = ContentBasedPreferenceGateway.getPreferencesString(contentTypeId, CommonEncodingPreferenceNames.INPUT_CODESET); + } + + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + return enc; + } + + private static final String getWorkbenchSpecifiedDefaultEncoding() { + ResourcesPlugin resourcePlugin = ResourcesPlugin.getPlugin(); + String enc = resourcePlugin.getPluginPreferences().getString(ResourcesPlugin.PREF_ENCODING); + // return blank as null + if (enc != null && enc.trim().length() == 0) { + enc = null; + } + return enc; + } + + /** + * @param specDefault + * This is the default charset name that would ordinarily be + * used for a particular type of content. Null may be + * specififed for those types with no spec default. If the spec + * default is known (and passed in), then it will be returned + * after being checked to see if there's be any user specified + * "override" for that charset (which would be rare). In other + * words, if the spec is known, there's little reason to use + * this method. + * @return the charset that should be used according to the rules + * established by this class. + */ + public static final String useDefaultNameRules(String specDefault) { + String enc = null; + String result = null; + enc = specDefault; + if (enc != null) { + result = enc; + } else { + enc = ContentTypeEncodingPreferences.getUserSpecifiedDefaultEncodingPreference(); + if (enc != null && enc.trim().length() > 0) { + result = enc.trim(); + } else { + if (enc == null || enc.trim().length() == 0) { + enc = getWorkbenchSpecifiedDefaultEncoding(); + if (enc != null) { + result = enc.trim(); + } + } + if (enc == null || enc.trim().length() == 0) { + enc = getJavaPlatformDefaultEncoding(); + // enc should never be null (but we'll + // check anyway) + if (enc != null) { + result = enc; + } + } + } + } + result = CodedIO.checkMappingOverrides(result); + return result; + } + + private NonContentBasedEncodingRules() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/Assert.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/Assert.java new file mode 100644 index 0000000..4e1b370 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/Assert.java
@@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +/** + * <code>Assert</code> is useful for for embedding runtime sanity checks in + * code. The predicate methods all test a condition and throw some type of + * unchecked exception if the condition does not hold. + * <p> + * Assertion failure exceptions, like most runtime exceptions, are thrown when + * something is misbehaving. Assertion failures are invariably unspecified + * behavior; consequently, clients should never rely on these being thrown + * (and certainly should not being catching them specifically). + * </p> + */ +public final class Assert { + + /** + * <code>AssertionFailedException</code> is a runtime exception thrown + * by some of the methods in <code>Assert</code>. + * <p> + * This class is not declared public to prevent some misuses; programs + * that catch or otherwise depend on assertion failures are susceptible to + * unexpected breakage when assertions in the code are added or removed. + * </p> + */ + static class AssertionFailedException extends RuntimeException { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception. + */ + public AssertionFailedException() { + super(); + } + + /** + * Constructs a new exception with the given message. + */ + public AssertionFailedException(String detail) { + super(detail); + } + } + + /** + * Asserts that an argument is legal. If the given boolean is not + * <code>true</code>, an <code>IllegalArgumentException</code> is + * thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the outcode of the check + * @param message + * the message to include in the exception + * @return <code>true</code> if the check passes (does not return if the + * check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression, String message) { + if (!expression) + throw new AssertionFailedException(message); + return expression; + } + + /** + * Asserts that the given object is not <code>null</code>. If this is + * not the case, some kind of unchecked exception is thrown. The given + * message is included in that exception, to aid debugging. + * + * @param object + * the value to test + * @param message + * the message to include in the exception + * @exception IllegalArgumentException + * if the object is <code>null</code> + */ + public static void isNotNull(Object object, String message) { + if (object == null) { + throw new AssertionFailedException(); + } + } + + /** + * Asserts that the given boolean is <code>true</code>. If this is not + * the case, some kind of unchecked exception is thrown. The given message + * is included in that exception, to aid debugging. + * + * @param expression + * the outcode of the check + * @param message + * the message to include in the exception + * @return <code>true</code> if the check passes (does not return if the + * check fails) + */ + public static boolean isTrue(boolean expression, String message) { + if (!expression) { + throw new AssertionFailedException(); + } + return expression; + } + + /* This class is not intended to be instantiated. */ + private Assert() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/BufferedLimitedReader.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/BufferedLimitedReader.java new file mode 100644 index 0000000..f026bda --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/BufferedLimitedReader.java
@@ -0,0 +1,58 @@ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +/** + * This is a pretty limited implementation, sort of specific + * to the way its used by tokenizers (JFlex). To really + * be general purpose, would need more work. + * + */ + + +public class BufferedLimitedReader extends BufferedReader { + private int limitedCount; + private int nRead; + + public BufferedLimitedReader(Reader reader, int size) { + super(reader, size); + if (reader.markSupported()) { + try { + mark(size); + } + catch (IOException e) { + // impossible + e.printStackTrace(); + } + } + limitedCount = size; + } + + public int read() throws IOException { + int result = 0; + nRead++; + if (nRead > limitedCount) { + result = -1; + } + else { + result = super.read(); + } + return result; + + } + + public int read(char cbuf[], int off, int len) throws IOException { + int result = 0; + if (nRead + len > limitedCount) { + result = -1; + } + else { + result = super.read(cbuf, off, len); + nRead = nRead + result; + } + return result; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/BufferedLimitedStream.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/BufferedLimitedStream.java new file mode 100644 index 0000000..8928655 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/BufferedLimitedStream.java
@@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class BufferedLimitedStream extends BufferedInputStream { + + private int limitedCount; + + public BufferedLimitedStream(InputStream inStream, int size) { + super(inStream, size); + mark(size); + try { + limitedCount = Math.min(size, inStream.available()); + } catch (IOException e) { + // unlikely + limitedCount = 0; + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#available() + */ + public synchronized int available() throws IOException { + + return limitedCount - pos; + } + + /** + * copied down from super class + */ + private void ensureOpen() throws IOException { + if (in == null) + throw new IOException("Stream closed"); //$NON-NLS-1$ + } + + /** + * copied down from super class then, changed to simiulate EOF if goes + * beyond buffered amount + */ + public synchronized int read() throws IOException { + ensureOpen(); + // for this special stream, indicate "end of file" when buffer is + // full + if (pos >= limitedCount) { + return -1; + } + return super.read(); + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read(byte[], int, int) + */ + public synchronized int read(byte[] b, int off, int len) throws IOException { + // for this special stream, indicate "end of file" when buffer is + // full + if (pos >= limitedCount) { + return -1; + } + return super.read(b, off, len); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/ByteReader.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/ByteReader.java new file mode 100644 index 0000000..35046e2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/ByteReader.java
@@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; + +/** + * This is an "adapter" class, simply to get in input stream to act like a + * reader. We could not use InputStreamReader directly because its internal + * buffers are not controllable, and it sometimes pulls too much out of input + * stream (even when it wasn't needed for our purposes). + * + * The use of this class is highly specialized and by not means meant to be + * general purpose. Its use is restricted to those cases where the input + * stream can be regarded as ascii just long enough to determine what the real + * encoding should be. + */ + +public class ByteReader extends Reader { + + + public static final int DEFAULT_BUFFER_SIZE = CodedIO.MAX_BUF_SIZE; + + protected byte[] fBuffer; + + protected InputStream fInputStream; + + protected ByteReader() { + super(); + } + + public ByteReader(InputStream inputStream) { + this(inputStream, DEFAULT_BUFFER_SIZE); + if (!inputStream.markSupported()) { + throw new IllegalArgumentException("ByteReader is required to have a resettable stream"); //$NON-NLS-1$ + } + } + + public ByteReader(InputStream inputStream, int size) { + this.fInputStream = inputStream; + if (!inputStream.markSupported()) { + throw new IllegalArgumentException("ByteReader is required to have a resettable stream"); //$NON-NLS-1$ + } + this.fBuffer = new byte[size]; + + } + + public void close() throws IOException { + this.fInputStream.close(); + } + + public void mark(int readAheadLimit) { + this.fInputStream.mark(readAheadLimit); + } + + public boolean markSupported() { + return true; + } + + public int read() throws IOException { + int b0 = this.fInputStream.read(); + return (b0 & 0x00FF); + } + + public int read(char ch[], int offset, int length) throws IOException { + if (length > this.fBuffer.length) { + length = this.fBuffer.length; + } + + int count = this.fInputStream.read(this.fBuffer, 0, length); + + for (int i = 0; i < count; i++) { + int b0 = this.fBuffer[i]; + // the 0x00FF is to "lose" the negative bits filled in the byte to + // int conversion + // (and which would be there if cast directly from byte to char). + char c0 = (char) (b0 & 0x00FF); + ch[offset + i] = c0; + } + return count; + } + + public boolean ready() throws IOException { + return this.fInputStream.available() > 0; + } + + public void reset() throws IOException { + this.fInputStream.reset(); + } + + public long skip(long n) throws IOException { + return this.fInputStream.skip(n); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/CodedResourcePlugin.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/CodedResourcePlugin.java new file mode 100644 index 0000000..0c928cd --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/CodedResourcePlugin.java
@@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Preferences; +import org.eclipse.wst.sse.core.internal.encoding.CommonEncodingPreferenceNames; +import org.eclipse.wst.sse.core.internal.encoding.ICodedResourcePlugin; + +/** + * The main plugin class to be used in the desktop. + */ +public class CodedResourcePlugin extends Plugin implements ICodedResourcePlugin { + //The shared instance. + private static CodedResourcePlugin plugin; + + /** + * Returns the shared instance. + */ + public static CodedResourcePlugin getDefault() { + return plugin; + } + + /** + * Returns the workspace instance. + */ + public static IWorkspace getWorkspace() { + return ResourcesPlugin.getWorkspace(); + } + + /** + * The constructor. + */ + public CodedResourcePlugin() { + super(); + plugin = this; + } + + protected void initializeDefaultPluginPreferences() { + Preferences prefs = getPluginPreferences(); + prefs.setDefault(CommonEncodingPreferenceNames.USE_3BYTE_BOM_WITH_UTF8, false); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/Logger.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/Logger.java new file mode 100644 index 0000000..9e911b9 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/Logger.java
@@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import com.ibm.icu.util.StringTokenizer; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; + + +/** + * Small convenience class to log messages to plugin's log file and also, if + * desired, the console. This class should only be used by classes in this + * plugin. Other plugins should make their own copy, with appropriate ID. + */ +public class Logger { + private static final String PLUGIN_ID = "org.eclipse.wst.sse.core.internal.encoding"; //$NON-NLS-1$ + + public static final int ERROR = IStatus.ERROR; // 4 + public static final int ERROR_DEBUG = 200 + ERROR; + public static final int INFO = IStatus.INFO; // 1 + public static final int INFO_DEBUG = 200 + INFO; + + public static final int OK = IStatus.OK; // 0 + + public static final int OK_DEBUG = 200 + OK; + + private static final String TRACEFILTER_LOCATION = "/debug/tracefilter"; //$NON-NLS-1$ + public static final int WARNING = IStatus.WARNING; // 2 + public static final int WARNING_DEBUG = 200 + WARNING; + + /** + * Adds message to log. + * + * @param level + * severity level of the message (OK, INFO, WARNING, ERROR, + * OK_DEBUG, INFO_DEBUG, WARNING_DEBUG, ERROR_DEBUG) + * @param message + * text to add to the log + * @param exception + * exception thrown + */ + protected static void _log(int level, String message, Throwable exception) { + if (level == OK_DEBUG || level == INFO_DEBUG || level == WARNING_DEBUG || level == ERROR_DEBUG) { + if (!isDebugging()) + return; + } + + int severity = IStatus.OK; + switch (level) { + case INFO_DEBUG : + case INFO : + severity = IStatus.INFO; + break; + case WARNING_DEBUG : + case WARNING : + severity = IStatus.WARNING; + break; + case ERROR_DEBUG : + case ERROR : + severity = IStatus.ERROR; + } + message = (message != null) ? message : "null"; //$NON-NLS-1$ + Status statusObj = new Status(severity, PLUGIN_ID, severity, message, exception); + Bundle bundle = Platform.getBundle(PLUGIN_ID); + if (bundle != null) + Platform.getLog(bundle).log(statusObj); + } + + /** + * Prints message to log if category matches /debug/tracefilter option. + * + * @param message + * text to print + * @param category + * category of the message, to be compared with + * /debug/tracefilter + */ + protected static void _trace(String category, String message, Throwable exception) { + if (isTracing(category)) { + message = (message != null) ? message : "null"; //$NON-NLS-1$ + Status statusObj = new Status(IStatus.OK, PLUGIN_ID, IStatus.OK, message, exception); + Bundle bundle = Platform.getBundle(PLUGIN_ID); + if (bundle != null) + Platform.getLog(bundle).log(statusObj); + } + } + + /** + * @return true if the platform is debugging + */ + public static boolean isDebugging() { + return Platform.inDebugMode(); + } + + /** + * Determines if currently tracing a category + * + * @param category + * @return true if tracing category, false otherwise + */ + public static boolean isTracing(String category) { + if (!isDebugging()) + return false; + + String traceFilter = Platform.getDebugOption(PLUGIN_ID + TRACEFILTER_LOCATION); + if (traceFilter != null) { + StringTokenizer tokenizer = new StringTokenizer(traceFilter, ","); //$NON-NLS-1$ + while (tokenizer.hasMoreTokens()) { + String cat = tokenizer.nextToken().trim(); + if (category.equals(cat)) { + return true; + } + } + } + return false; + } + + public static void log(int level, String message) { + _log(level, message, null); + } + + public static void log(int level, String message, Throwable exception) { + _log(level, message, exception); + } + + public static void logException(String message, Throwable exception) { + _log(ERROR, message, exception); + } + + public static void logException(Throwable exception) { + _log(ERROR, exception.getMessage(), exception); + } + + public static void trace(String category, String message) { + _trace(category, message, null); + } + + public static void traceException(String category, String message, Throwable exception) { + _trace(category, message, exception); + } + + public static void traceException(String category, Throwable exception) { + _trace(category, exception.getMessage(), exception); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/NullInputStream.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/NullInputStream.java new file mode 100644 index 0000000..6b44f2f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/NullInputStream.java
@@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import java.io.IOException; +import java.io.InputStream; + + +public class NullInputStream extends InputStream { + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#mark(int) + */ + public synchronized void mark(int readlimit) { + // nothing to do + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#markSupported() + */ + public boolean markSupported() { + // we can mark nothing. + // and, we are using this Null class specifically for + // a "fake" resettable stream. + return true; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read() + */ + public int read() throws IOException { + + return -1; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#reset() + */ + public synchronized void reset() throws IOException { + // nothing to do + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#skip(long) + */ + public long skip(long n) throws IOException { + return 0; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/ResourceBundleHelper.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/ResourceBundleHelper.java new file mode 100644 index 0000000..14aaa1f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/ResourceBundleHelper.java
@@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Locale; +import java.util.ResourceBundle; + +import org.eclipse.core.runtime.Platform; + +//TODO: rework this with new platform/runtime APIs (if still needed). + +public class ResourceBundleHelper { + + public static ResourceBundle getResourceBundle(String resourceURI) throws MalformedURLException, IOException { + return getResourceBundle(resourceURI, Locale.getDefault()); + } + + public static ResourceBundle getResourceBundle(String resourceURI, Locale targetLocale) throws MalformedURLException, IOException { + // try to load bundle from the location specified in the resourceURI + // we make the assumption that the resourceURI points to the local + // file system + + int index = resourceURI.lastIndexOf("/"); //$NON-NLS-1$ + if (index == -1) { + throw new IllegalArgumentException("Invalid resourceURI"); //$NON-NLS-1$ + } + + // Below we set 'resourceDirectory' so that it ends with a '/'. + // Here's an excerpt from the ClassLoader Javadoc ... + // Any URL that ends with a '/' is assumed to refer to a directory. + // Otherwise, the URL is assumed + // to refer to a JAR file which will be opened as needed. + // + String resourceDirectory = resourceURI.substring(0, index + 1); + String resourceBundleName = resourceURI.substring(index + 1); + + // create a class loader with a class path that points to the resource + // bundle's location + // + URL[] classpath = new URL[1]; + classpath[0] = Platform.resolve(new URL(resourceDirectory)); + ClassLoader resourceLoader = new URLClassLoader(classpath, null); + + return ResourceBundle.getBundle(resourceBundleName, targetLocale, resourceLoader); + } +} +
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/UnicodeBOMEncodingDetector.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/UnicodeBOMEncodingDetector.java new file mode 100644 index 0000000..e3c9d99 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/encoding/util/UnicodeBOMEncodingDetector.java
@@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.encoding.util; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.Charset; + +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.encoding.IResourceCharsetDetector; + + +/** + * This is a "common function" class to decide if an input stream, is a + * unicode stream. + */ +public class UnicodeBOMEncodingDetector implements IResourceCharsetDetector { + + //private static final String UTF_16_CHARSET_NAME = "UTF-16"; + // //$NON-NLS-1$ + + public static class NotEnoughInputForBOMException extends IOException { + + /** + * Default <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + public NotEnoughInputForBOMException() { + super(); + } + + public NotEnoughInputForBOMException(String s) { + super(s); + } + + } + + private final static byte BB = (byte) 0xBB; + private final static byte BF = (byte) 0xBF; + private final static byte EF = (byte) 0xEF; + private final static byte FE = (byte) -2; + + private final static byte FF = (byte) -1; + private static final String UTF_16BE_CHARSET_NAME = "UTF-16BE"; //$NON-NLS-1$ + private static final String UTF_16LE_CHARSET_NAME = "UTF-16LE"; //$NON-NLS-1$ + + private static final String UTF_8_CHARSET_NAME = "UTF-8"; //$NON-NLS-1$ + + private InputStream fInputStream = null; + private boolean fNoBOMPossible; + + private EncodingMemento checkForBOM(InputStream inputStream) { + EncodingMemento result = null; + + try { + byte b1 = getNextByte(inputStream); + byte b2 = getNextByte(inputStream); + if (b1 == FE && b2 == FF) { + result = createEncodingMemento(UTF_16BE_CHARSET_NAME); + result.setUnicodeStream(true); + } else { + if (b1 == FF && b2 == FE) { + result = createEncodingMemento(UTF_16LE_CHARSET_NAME); + result.setUnicodeStream(true); + } else { + byte b3 = getNextByte((inputStream)); + if (b1 == EF && b2 == BB && b3 == BF) { + result = createEncodingMemento(UTF_8_CHARSET_NAME); + result.setUTF83ByteBOMUsed(true); + } + } + } + } catch (NotEnoughInputForBOMException e) { + // This is sort of unexpected for normal cases, but can occur for + // empty + // streams. And, this can occur "normally" for non-BOM streams + // that + // have only two + // bytes, and for which those two bytes match the first two bytes + // of UTF-8 + // BOM In any case, we'll simply return null; + result = null; + } catch (IOException e) { + // other errors should be impossible + throw new Error(e); + } + + return result; + } + + private EncodingMemento createEncodingMemento(String javaEncodingName) { + EncodingMemento encodingMemento = new EncodingMemento(); + encodingMemento.setJavaCharsetName(javaEncodingName); + String ianaName = Charset.forName(javaEncodingName).name(); + encodingMemento.setDetectedCharsetName(ianaName); + if (javaEncodingName.equals(UTF_8_CHARSET_NAME)) { + encodingMemento.setUTF83ByteBOMUsed(true); + } + return encodingMemento; + } + + public String getEncoding() throws IOException { + + return getEncodingMemento().getDetectedCharsetName(); + } + + /** + * Returns IANA encoding name if BOM detected in stream. If a BOM is + * detected, the stream is left positioned after readying the BOM. If a + * BOM is not detected, the steam is reset. + * + * 0xFEFF UTF-16, big-endian 0xFFFE UTF-16, little-endian 0xEFBBBF UTF-8 + * (BOM is optional) + * + * @param inputStream - + * must be a resetable (mark supported) stream so it can be + * reset, if not BOM encoded stream + * @return String - IANA encodingname (may not work well on 1.3, but 1.4 + * seems to have good support for IANA names) + */ + public EncodingMemento getEncodingMemento() { + + EncodingMemento result = null; + if (!fNoBOMPossible) { + + if (fInputStream == null) + throw new IllegalStateException("input must be set before use"); //$NON-NLS-1$ + + if (!fInputStream.markSupported()) { + throw new IllegalArgumentException("inputStream must be resetable"); //$NON-NLS-1$ + } + + result = checkForBOM(fInputStream); + } + + return result; + + } + + private byte getNextByte(InputStream inputStream) throws IOException { + + int byteCharAsInt = -1; + // be sure we won't block + if (inputStream.available() > 0) { + byteCharAsInt = inputStream.read(); + byteCharAsInt = byteCharAsInt & 0XFF; + } + // to avoid confustion over meaning of returned byte, + // throw exception if EOF reached. + if (byteCharAsInt == -1) + throw new NotEnoughInputForBOMException("typically not an error"); //$NON-NLS-1$ + return (byte) byteCharAsInt; + } + + /** + * + */ + + public String getSpecDefaultEncoding() { + // There is no default for this case + return null; + } + + /** + * + */ + private void resetAll() { + fNoBOMPossible = false; + fInputStream = null; + + } + + /** + * + */ + + public void set(InputStream inputStream) { + resetAll(); + fInputStream = inputStream; + } + + public void set(IStorage iStorage) throws CoreException { + set(new BufferedInputStream(iStorage.getContents(), CodedIO.MAX_BUF_SIZE)); + + } + + public void set(Reader reader) { + if (reader instanceof ByteReader) { + ByteReader byteReader = (ByteReader) reader; + fInputStream = byteReader.fInputStream; + } else { + fNoBOMPossible = true; + } + + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/CharConversionErrorWithDetail.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/CharConversionErrorWithDetail.java new file mode 100644 index 0000000..1f19444 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/CharConversionErrorWithDetail.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.exceptions; + +import java.nio.charset.CharacterCodingException; + + +public class CharConversionErrorWithDetail extends CharacterCodingException { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + private String fCharsetName; + + public CharConversionErrorWithDetail() { + super(); + } + + /** + * @param s + */ + public CharConversionErrorWithDetail(String charsetName) { + super(); + fCharsetName = charsetName; + } + + /** + * @return Returns the fCharsetName. + */ + public String getCharsetName() { + return fCharsetName; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/MalformedInputExceptionWithDetail.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/MalformedInputExceptionWithDetail.java new file mode 100644 index 0000000..ff31bbd --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/MalformedInputExceptionWithDetail.java
@@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.exceptions; + +import java.nio.charset.CharacterCodingException; + + +/** + * Intended to be a more precise form of the MalformedInputException, where + * character position and attempted encoding can be attempted. + */ +public class MalformedInputExceptionWithDetail extends CharacterCodingException { + + /** + * Default <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + private int fCharPosition; + private String fDetectedCharsetName; + private boolean fExceededMax = false; + private String fJavaCharsetName; + private int fMaxBuffer; + + /** + * Disallow default constructor. If attemptedEncoding and charPostion can + * not be provided, use one of java's MalformedException. + */ + protected MalformedInputExceptionWithDetail() { + // Nothing to do + } + + public MalformedInputExceptionWithDetail(String encodingName, int charPostion) { + this.fJavaCharsetName = encodingName; + this.fDetectedCharsetName = encodingName; + this.fCharPosition = charPostion; + } + + public MalformedInputExceptionWithDetail(String attemptedJavaEncoding, String attemptedIANAEncoding, int charPostion) { + this.fJavaCharsetName = attemptedJavaEncoding; + this.fDetectedCharsetName = attemptedIANAEncoding; + this.fCharPosition = charPostion; + } + + /** + * If charPosition = -1 this could be because the character position + * exceeded the maximum buffer size, maxBuffer, then exceededMax = true. + */ + public MalformedInputExceptionWithDetail(String attemptedJavaEncoding, String attemptedIANAEncoding, int charPostion, boolean exceededMax, int maxBuffer) { + this.fJavaCharsetName = attemptedJavaEncoding; + this.fDetectedCharsetName = attemptedIANAEncoding; + this.fCharPosition = charPostion; + this.fExceededMax = exceededMax; + this.fMaxBuffer = maxBuffer; + } + + /** + */ + public java.lang.String getAttemptedIANAEncoding() { + return fDetectedCharsetName; + } + + /** + */ + public java.lang.String getAttemptedJavaEncoding() { + return fJavaCharsetName; + } + + /** + * @return int + */ + public int getCharPosition() { + return fCharPosition; + } + + /** + * Returns the maxBuffer. + * + * @return int + */ + public int getMaxBuffer() { + return fMaxBuffer; + } + + /** + * Returns the exceededMax. + * + * @return boolean + */ + public boolean isExceededMax() { + return fExceededMax; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/MalformedOutputExceptionWithDetail.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/MalformedOutputExceptionWithDetail.java new file mode 100644 index 0000000..6eec680 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/MalformedOutputExceptionWithDetail.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.exceptions; + + +public class MalformedOutputExceptionWithDetail extends MalformedInputExceptionWithDetail { + + /** + * Default <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + + /** + * Constructor for MalformedOutputExceptionWithDetail. If attemptedEncoding and charPostion can + * not be provided, use sun.io.MalformedException. + * + * @param attemptedJavaEncoding + * @param attemptedIANAEncoding + * @param charPostion + */ + public MalformedOutputExceptionWithDetail(String attemptedJavaEncoding, String attemptedIANAEncoding, int charPostion) { + super(attemptedJavaEncoding, attemptedIANAEncoding, charPostion); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/UnsupportedCharsetExceptionWithDetail.java b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/UnsupportedCharsetExceptionWithDetail.java new file mode 100644 index 0000000..123af87 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-encoding/org/eclipse/wst/sse/core/internal/exceptions/UnsupportedCharsetExceptionWithDetail.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.exceptions; + +import java.nio.charset.UnsupportedCharsetException; + +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; + + +/** + * This is intended for same purpose as it super class, but simply provides + * more information about than the name in error. This is especially useful + * for "UIs" which can present users with the error, and the + * "appropriateDefault" that can be used for a particular input. + */ +public class UnsupportedCharsetExceptionWithDetail extends UnsupportedCharsetException { + + /** + * Default <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + private EncodingMemento fEncodingMementio; + + public UnsupportedCharsetExceptionWithDetail(EncodingMemento encodingMemento) { + this(encodingMemento.getDetectedCharsetName()); + fEncodingMementio = encodingMemento; + } + + protected UnsupportedCharsetExceptionWithDetail(String charsetName) { + super(charsetName); + } + + public EncodingMemento getEncodingMemento() { + return fEncodingMementio; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/provisional/tasks/IFileTaskScanner.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/provisional/tasks/IFileTaskScanner.java new file mode 100644 index 0000000..d1655b2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/provisional/tasks/IFileTaskScanner.java
@@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.tasks; + +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; + +/** + * Scanners for the main Task Scanner. Scanners may be contributed using the + * org.eclipse.wst.sse.core.taskscanner extension point. For resources and + * resource deltas with matching content types, the main scanner will first + * call the startup() method, scan(), and then shutdown() in sequence. Scanner + * instances will be reused across projects but are not shared per content + * type. Scanners should not hold on to references to models or resources + * after shutdown() and should take care not to leak memory or resources. + */ +public interface IFileTaskScanner { + /** + * Default marker type ID of task markers that are created. + */ + String TASK_MARKER_ID = SSECorePlugin.ID + ".task"; //$NON-NLS-1$; + + /** + * @return the task marker type that should be removed each time tasks are + * rescanned and used by default when creating task markers. + * Children of this marker type will be removed automatically. + */ + String getMarkerType(); + + /** + * Requests that the list of automatically discovered tasks for the given + * file be updated. Once completed, the list of tasks should correspond + * exactly to the file's contents. + * + * @param file - + * the file to be scanned + * @param taskTags - + * the list of task tags for which to scan + * @param monitor - + * a progress monitor + * @return an array of maps containing the attributes of task markers to + * be created + * <p> + * The reserved attribute name + * <b>org.eclipse.core.resources.taskmarker</b> may be used to + * specify a type to use when creating the task marker. + * </p> + */ + Map[] scan(IFile file, TaskTag[] taskTags, IProgressMonitor monitor); + + /** + * Notifies the scanner that scanning is done for now. Resources held from + * startup should now be released. + * + * @param project - + * the project that was just scanned + */ + void shutdown(IProject project); + + /** + * Notifies the scanner that a sequence of scans is about to be requested. + * Ideally the time to load preferences and perform any expensive + * configuration for the given project. + * + * @param project - + * the project that is about to be scanned + * + */ + void startup(IProject project); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/provisional/tasks/TaskTag.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/provisional/tasks/TaskTag.java new file mode 100644 index 0000000..cab7258 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/provisional/tasks/TaskTag.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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.wst.sse.core.internal.provisional.tasks; + +import org.eclipse.core.resources.IMarker; + +/** + * Simple representation of the values that make up a Task Tag + */ +public final class TaskTag { + + public static final int PRIORITY_HIGH = IMarker.PRIORITY_HIGH; + public static final int PRIORITY_LOW = IMarker.PRIORITY_LOW; + public static final int PRIORITY_NORMAL = IMarker.PRIORITY_NORMAL; + + /** + * this task tag's priority + */ + private int fPriority = PRIORITY_NORMAL; + + /** + * this task tag's "tagging" text + */ + private String fTag = null; + + public TaskTag(String tag, int priority) { + super(); + fTag = tag; + fPriority = priority; + } + + public int getPriority() { + return fPriority; + } + + public String getTag() { + return fTag; + } + + public String toString() { + return getTag() + ":" + getPriority(); //$NON-NLS-1$ + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/FileTaskScannerRegistryReader.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/FileTaskScannerRegistryReader.java new file mode 100644 index 0000000..34b9c07 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/FileTaskScannerRegistryReader.java
@@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300434 - Make inner classes static where possibl * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.tasks; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.internal.provisional.tasks.IFileTaskScanner; +import org.eclipse.wst.sse.core.utils.StringUtils; + +public class FileTaskScannerRegistryReader { + private static class ScannerInfo { + String fId; + IFileTaskScanner fScanner; + + ScannerInfo(String id, IFileTaskScanner scanner) { + super(); + fId = id; + fScanner = scanner; + } + + public boolean equals(Object obj) { + return obj instanceof ScannerInfo && fId.equals(((ScannerInfo) obj).fId); + } + + public IFileTaskScanner getScanner() { + return fScanner; + } + } + + private static FileTaskScannerRegistryReader _instance = null; + + public static FileTaskScannerRegistryReader getInstance() { + if (_instance == null) { + _instance = new FileTaskScannerRegistryReader(); + } + return _instance; + } + + private String ATT_CLASS = "class"; //$NON-NLS-1$ + + private String ATT_CONTENT_TYPES = "contentTypeIds"; //$NON-NLS-1$ + + private String ATT_ID = "id"; //$NON-NLS-1$ + + private IConfigurationElement[] fScannerElements; + + // a mapping from content types to ScannerInfo instances + private Map fScannerInfos = null; + + private String NAME_SCANNER = "scanner"; //$NON-NLS-1$ + + private String SCANNER_EXTENSION_POINT_ID = SSECorePlugin.ID + ".taskscanner"; //$NON-NLS-1$ + + private FileTaskScannerRegistryReader() { + super(); + } + + IFileTaskScanner[] getFileTaskScanners(IContentType[] contentTypes) { + if (fScannerElements == null) { + readRegistry(); + } + + List scannerInfos = new ArrayList(1); + + for (int i = 0; i < contentTypes.length; i++) { + ScannerInfo[] scannerInfosForContentType = (ScannerInfo[]) fScannerInfos.get(contentTypes[i].getId()); + if (scannerInfosForContentType == null) { + scannerInfosForContentType = loadScanners(contentTypes[i]); + } + // only add non-duplicate scanners + for (int j = 0; j < scannerInfosForContentType.length; j++) { + if (!scannerInfos.contains(scannerInfosForContentType[j])) { + scannerInfos.add(scannerInfosForContentType[j]); + } + } + } + IFileTaskScanner[] scanners = new IFileTaskScanner[scannerInfos.size()]; + for (int i = 0; i < scanners.length; i++) { + scanners[i] = ((ScannerInfo) scannerInfos.get(i)).getScanner(); + } + return scanners; + } + + public String[] getSupportedContentTypeIds() { + if (fScannerElements == null) { + readRegistry(); + } + + // find the relevant extensions + List types = new ArrayList(0); + IConfigurationElement[] scannerElements = fScannerElements; + for (int j = 0; j < scannerElements.length; j++) { + if (!scannerElements[j].getName().equals(NAME_SCANNER)) + continue; + String[] contentTypeIds = StringUtils.unpack(scannerElements[j].getAttribute(ATT_CONTENT_TYPES)); + for (int i = 0; i < contentTypeIds.length; i++) { + if (!types.contains(contentTypeIds[i])) { + types.add(contentTypeIds[i]); + } + } + } + + return (String[]) types.toArray(new String[types.size()]); + } + + private ScannerInfo[] loadScanners(IContentType contentType) { + List elements = new ArrayList(0); + ScannerInfo[] scannerInfos = null; + IConfigurationElement[] delegateElements = fScannerElements; + if (contentType != null) { + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); + for (int j = 0; j < delegateElements.length; j++) { + if (!delegateElements[j].getName().equals(NAME_SCANNER)) + continue; + String[] supportedContentTypeIds = StringUtils.unpack(delegateElements[j].getAttribute(ATT_CONTENT_TYPES)); + IContentType[] supportedContentTypes = new IContentType[supportedContentTypeIds.length]; + for (int k = 0; k < supportedContentTypeIds.length; k++) { + supportedContentTypes[k] = contentTypeManager.getContentType(supportedContentTypeIds[k].trim()); + } + for (int k = 0; k < supportedContentTypeIds.length; k++) { + // allow subtypes to be returned as well + if (supportedContentTypes[k] != null && contentType.isKindOf(supportedContentTypes[k])) { + elements.add(delegateElements[j]); + } + } + } + // instantiate and save the scanners + List scannerInfoList = new ArrayList(elements.size()); + for (int i = 0; i < elements.size(); i++) { + try { + IFileTaskScanner scanner = (IFileTaskScanner) ((IConfigurationElement) elements.get(i)).createExecutableExtension(ATT_CLASS); + if (scanner != null) { + scannerInfoList.add(new ScannerInfo(((IConfigurationElement) elements.get(i)).getAttribute(ATT_ID), scanner)); + } + } + catch (CoreException e) { + Logger.logException("Non-fatal exception creating task scanner for " + contentType.getId(), e); //$NON-NLS-1$ + } + } + scannerInfos = (ScannerInfo[]) scannerInfoList.toArray(new ScannerInfo[scannerInfoList.size()]); + fScannerInfos.put(contentType.getId(), scannerInfos); + if (Logger.DEBUG_TASKSREGISTRY) { + System.out.println("Created " + scannerInfos.length + " task scanner for " + contentType.getId()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + return scannerInfos; + } + + private void readRegistry() { + fScannerInfos = new HashMap(); + // Just remember the elements, so plugins don't have to be activated, + // unless extension attributes match those of interest + IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(SCANNER_EXTENSION_POINT_ID); + if (point != null) { + fScannerElements = point.getConfigurationElements(); + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/StructuredFileTaskScanner.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/StructuredFileTaskScanner.java new file mode 100644 index 0000000..42271b4 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/StructuredFileTaskScanner.java
@@ -0,0 +1,329 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.tasks; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.CharacterCodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.document.DocumentReader; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionHandler; +import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionParser; +import org.eclipse.wst.sse.core.internal.modelhandler.ModelHandlerRegistry; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; +import org.eclipse.wst.sse.core.internal.provisional.tasks.IFileTaskScanner; +import org.eclipse.wst.sse.core.internal.provisional.tasks.TaskTag; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +/** + * A delegate to create IMarker.TASKs for "todos" and similar comments. + */ +public abstract class StructuredFileTaskScanner implements IFileTaskScanner, IExecutableExtension { + // the list of attributes for the new tasks for the current file + protected List fNewMarkerAttributeMaps = null; + + List oldMarkers = null; + private long time0; + private String runtimeMarkerType; + + public StructuredFileTaskScanner() { + super(); + fNewMarkerAttributeMaps = new ArrayList(); + if (Logger.DEBUG_TASKS) { + System.out.println(getClass().getName() + " instance created"); //$NON-NLS-1$ + } + } + + /** + * Returns the attributes with which a newly created marker will be + * initialized. Modified from the method in MarkerRulerAction + * + * @return the initial marker attributes + */ + protected Map createInitialMarkerAttributes(String text, int documentLine, int startOffset, int length, int priority) { + Map attributes = new HashMap(6); + // marker line numbers are 1-based + attributes.put(IMarker.LINE_NUMBER, new Integer(documentLine + 1)); + attributes.put(IMarker.TASK, getMarkerType()); + attributes.put(IMarker.CHAR_START, new Integer(startOffset)); + attributes.put(IMarker.CHAR_END, new Integer(startOffset + length)); + attributes.put(IMarker.MESSAGE, text); + attributes.put(IMarker.USER_EDITABLE, Boolean.FALSE); + + switch (priority) { + case IMarker.PRIORITY_HIGH : { + attributes.put(IMarker.PRIORITY, new Integer(IMarker.PRIORITY_HIGH)); + } + break; + case IMarker.PRIORITY_LOW : { + attributes.put(IMarker.PRIORITY, new Integer(IMarker.PRIORITY_LOW)); + } + break; + default : { + attributes.put(IMarker.PRIORITY, new Integer(IMarker.PRIORITY_NORMAL)); + } + } + + return attributes; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.tasks.IFileTaskScanner#getMarkerType() + */ + public String getMarkerType() { + if (runtimeMarkerType != null) + return runtimeMarkerType; + + return org.eclipse.core.resources.IMarker.TASK; + } + + private String detectCharset(IFile file) { + if (file.getType() == IResource.FILE && file.isAccessible()) { + try { + return file.getCharset(true); + } + catch (CoreException e) { + Logger.logException(e); + } + } + return ResourcesPlugin.getEncoding(); + } + + /** + * @param document + * @param documentRegion + * @param comment + */ + protected void findTasks(IDocument document, TaskTag[] taskTags, IStructuredDocumentRegion documentRegion, ITextRegion comment) { + if (isCommentRegion(documentRegion, comment)) { + int startOffset = documentRegion.getStartOffset(comment); + int endOffset = documentRegion.getTextEndOffset(comment); + try { + int startLine = document.getLineOfOffset(startOffset); + int endLine = document.getLineOfOffset(endOffset); + for (int lineNumber = startLine; lineNumber <= endLine; lineNumber++) { + IRegion line = document.getLineInformation(lineNumber); + int begin = Math.max(startOffset, line.getOffset()); + int end = Math.min(endOffset, line.getOffset() + line.getLength()); + int length = end - begin; + + /* XXX: This generates a lot of garbage objects */ + + String commentedText = getCommentedText(document, begin, length); + String comparisonText = commentedText.toLowerCase(Locale.ENGLISH); + + for (int i = 0; i < taskTags.length; i++) { + int tagIndex = comparisonText.indexOf(taskTags[i].getTag().toLowerCase(Locale.ENGLISH)); + if (tagIndex >= 0) { + String markerDescription = commentedText.substring(tagIndex); + int markerOffset = begin + tagIndex; + int markerLength = end - markerOffset; + fNewMarkerAttributeMaps.add(createInitialMarkerAttributes(markerDescription, lineNumber, markerOffset, markerLength, taskTags[i].getPriority())); + } + } + } + } + catch (BadLocationException e) { + Logger.logException(e); + } + } + } + + private void findTasks(IFile file, final TaskTag[] taskTags, final IProgressMonitor monitor) { + try { + IModelHandler handler = ModelHandlerRegistry.getInstance().getHandlerFor(file); + + // records if the optimized streamish parse was possible + boolean didStreamParse = false; + final IEncodedDocument defaultDocument = handler.getDocumentLoader().createNewStructuredDocument(); + if (defaultDocument instanceof IStructuredDocument) { + RegionParser parser = ((IStructuredDocument) defaultDocument).getParser(); + if (parser instanceof StructuredDocumentRegionParser) { + didStreamParse = true; + String charset = detectCharset(file); + StructuredDocumentRegionParser documentParser = (StructuredDocumentRegionParser) parser; + final IDocument textDocument = new Document(); + setDocumentContent(textDocument, file.getContents(true), charset); + monitor.beginTask("", textDocument.getLength()); + documentParser.reset(new DocumentReader(textDocument)); + documentParser.addStructuredDocumentRegionHandler(new StructuredDocumentRegionHandler() { + public void nodeParsed(IStructuredDocumentRegion documentRegion) { + ITextRegionList regions = documentRegion.getRegions(); + for (int j = 0; j < regions.size(); j++) { + ITextRegion comment = regions.get(j); + findTasks(textDocument, taskTags, documentRegion, comment); + } + // disconnect the document regions + if (documentRegion.getPrevious() != null) { + documentRegion.getPrevious().setPrevious(null); + documentRegion.getPrevious().setNext(null); + } + if (monitor.isCanceled()) { + textDocument.set(""); //$NON-NLS-1$ + } + monitor.worked(documentRegion.getLength()); + } + + public void resetNodes() { + } + }); + documentParser.getDocumentRegions(); + } + } + if (!didStreamParse) { + // Use a StructuredDocument + IEncodedDocument document = handler.getDocumentLoader().createNewStructuredDocument(file); + monitor.beginTask("", document.getLength()); + if (document instanceof IStructuredDocument) { + IStructuredDocumentRegion documentRegion = ((IStructuredDocument) document).getFirstStructuredDocumentRegion(); + while (documentRegion != null) { + ITextRegionList regions = documentRegion.getRegions(); + for (int j = 0; j < regions.size(); j++) { + ITextRegion comment = regions.get(j); + findTasks(document, taskTags, documentRegion, comment); + } + monitor.worked(documentRegion.getLength()); + documentRegion = documentRegion.getNext(); + } + } + } + } + catch (CoreException e) { + Logger.logException("Exception with " + file.getFullPath().toString(), e); //$NON-NLS-1$ + } + catch (CharacterCodingException e) { + Logger.log(Logger.INFO, "StructuredFileTaskScanner encountered CharacterCodingException reading " + file.getFullPath()); //$NON-NLS-1$ + } + catch (Exception e) { + Logger.logException("Exception with " + file.getFullPath().toString(), e); //$NON-NLS-1$ + } + monitor.done(); + } + + protected String getCommentedText(IDocument document, int begin, int length) throws BadLocationException { + return document.get(begin, length); + } + + protected abstract boolean isCommentRegion(IStructuredDocumentRegion region, ITextRegion textRegion); + + public synchronized Map[] scan(IFile file, TaskTag[] taskTags, IProgressMonitor monitor) { + fNewMarkerAttributeMaps.clear(); + if (monitor.isCanceled() || !shouldScan(file)) { + return new Map[0]; + } + if (Logger.DEBUG_TASKSPERF) { + time0 = System.currentTimeMillis(); + } + if (taskTags.length > 0) { + findTasks(file, taskTags, monitor); + } + if (Logger.DEBUG_TASKSPERF) { + System.out.println("" + (System.currentTimeMillis() - time0) + "ms for " + file.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ + } + return (Map[]) fNewMarkerAttributeMaps.toArray(new Map[fNewMarkerAttributeMaps.size()]); + } + + /** + * Sets the document content from this stream and closes the stream + */ + protected void setDocumentContent(IDocument document, InputStream contentStream, String charset) { + Reader in = null; + try { + in = new BufferedReader(new InputStreamReader(contentStream, charset), 2048); + StringBuffer buffer = new StringBuffer(2048); + char[] readBuffer = new char[2048]; + int n = in.read(readBuffer); + while (n > 0) { + buffer.append(readBuffer, 0, n); + n = in.read(readBuffer); + } + document.set(buffer.toString()); + } + catch (IOException x) { + } + finally { + if (in != null) { + try { + in.close(); + } + catch (IOException x) { + } + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, + * java.lang.String, java.lang.Object) + */ + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { + if (data != null && data instanceof String) { + runtimeMarkerType = data.toString(); + } + } + + boolean shouldScan(IResource r) { + // skip "dot" files + String s = r.getName(); + return s.length() == 0 || s.charAt(0) != '.'; + } + + public void shutdown(IProject project) { + if (Logger.DEBUG_TASKS) { + System.out.println(this + " shutdown for " + project.getName()); //$NON-NLS-1$ + } + } + + public void startup(IProject project) { + if (Logger.DEBUG_TASKS) { + System.out.println(this + " startup for " + project.getName()); //$NON-NLS-1$ + } + if (Logger.DEBUG_TASKSPERF) { + time0 = System.currentTimeMillis(); + } + if (Logger.DEBUG_TASKSPERF) { + System.out.println("" + (System.currentTimeMillis() - time0) + "ms loading prefs for " + project.getName()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskScanningJob.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskScanningJob.java new file mode 100644 index 0000000..92fda63 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskScanningJob.java
@@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * David Carver (Intalio) - bug 300443 - some constants aren't static final + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.tasks; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IPreferencesService; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.osgi.framework.Bundle; + +/** + * Queueing Job for processing deltas and projects. + */ +class TaskScanningJob extends Job { + static final int JOB_DELAY_DELTA = 1000; + private static final int JOB_DELAY_PROJECT = 5000; + static final String TASK_TAG_PROJECTS_ALREADY_SCANNED = "task-tag-projects-already-scanned"; //$NON-NLS-1$ + private List fQueue = null; + + /** symbolic name for OSGI framework */ + private static final String OSGI_FRAMEWORK_ID = "org.eclipse.osgi"; //$NON-NLS-1$ + + TaskScanningJob() { + super(SSECoreMessages.TaskScanner_0); + fQueue = new ArrayList(); + setPriority(Job.DECORATE); + setSystem(true); + setUser(false); + + SSECorePlugin.getDefault().getPluginPreferences().setDefault(TASK_TAG_PROJECTS_ALREADY_SCANNED, ""); //$NON-NLS-1$ + } + + synchronized void addProjectDelta(IResourceDelta delta) { + IResource projectResource = delta.getResource(); + + if (projectResource.getType() == IResource.PROJECT) { + if (isEnabledOnProject((IProject) projectResource)) { + fQueue.add(delta); + if (Logger.DEBUG_TASKSJOB) { + String kind = null; + switch (delta.getKind()) { + case IResourceDelta.ADDED : + kind = " [IResourceDelta.ADDED]"; //$NON-NLS-1$ + break; + case IResourceDelta.CHANGED : + kind = " [IResourceDelta.CHANGED]"; //$NON-NLS-1$ + break; + case IResourceDelta.REMOVED : + kind = " [IResourceDelta.REMOVED]"; //$NON-NLS-1$ + break; + case IResourceDelta.ADDED_PHANTOM : + kind = " [IResourceDelta.ADDED_PHANTOM]"; //$NON-NLS-1$ + break; + case IResourceDelta.REMOVED_PHANTOM : + kind = " [IResourceDelta.REMOVED_PHANTOM]"; //$NON-NLS-1$ + break; + } + System.out.println("Adding delta " + delta.getFullPath() + kind); //$NON-NLS-1$ + } + schedule(JOB_DELAY_DELTA); + } + } + } + + synchronized void addProject(IProject project) { + if (projectHasNotBeenFullyScanned(project)) { + fQueue.add(project); + if (Logger.DEBUG_TASKSJOB) { + System.out.println("Adding project " + project.getName()); //$NON-NLS-1$ + } + schedule(JOB_DELAY_PROJECT); + } + } + + /** + * A check to see if the OSGI framework is shutting down. + * + * @return true if the System Bundle is stopped (ie. the framework is + * shutting down) + */ + boolean frameworkIsShuttingDown() { + // in the Framework class there's a note: + // set the state of the System Bundle to STOPPING. + // this must be done first according to section 4.19.2 from the OSGi + // R3 spec. + boolean shuttingDown = !Platform.isRunning() || Platform.getBundle(OSGI_FRAMEWORK_ID).getState() == Bundle.STOPPING; + if (Logger.DEBUG_TASKSJOB && shuttingDown) { + System.out.println("TaskScanningJob: system is shutting down!"); //$NON-NLS-1$ + } + return shuttingDown; + } + + private boolean isEnabledOnProject(IProject p) { + IPreferencesService preferencesService = Platform.getPreferencesService(); + IScopeContext[] lookupOrder = new IScopeContext[]{new ProjectScope(p), new InstanceScope(), new DefaultScope()}; + return preferencesService.getBoolean(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_ENABLE, false, lookupOrder); + } + + private boolean projectHasNotBeenFullyScanned(IResource project) { + String[] projectsScanned = StringUtils.unpack(SSECorePlugin.getDefault().getPluginPreferences().getString(TASK_TAG_PROJECTS_ALREADY_SCANNED)); + + boolean shouldScan = true; + String name = project.getName(); + for (int j = 0; shouldScan && j < projectsScanned.length; j++) { + if (projectsScanned[j].equals(name)) { + if (Logger.DEBUG_TASKSJOB) + System.out.println("Scanning Job skipping " + project.getName()); //$NON-NLS-1$ + shouldScan = false; + } + } + return shouldScan; + } + + synchronized List retrieveQueue() { + List queue = fQueue; + fQueue = new ArrayList(); + return queue; + } + + protected IStatus run(IProgressMonitor monitor) { + if (frameworkIsShuttingDown()) + return Status.CANCEL_STATUS; + + cleanupRememberedProjectList(TASK_TAG_PROJECTS_ALREADY_SCANNED); + + IStatus status = null; + List currentQueue = retrieveQueue(); + + try { + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + } + catch (SecurityException e) { + // not a critical problem + } + + List errors = null; + int ticks = currentQueue.size(); + String taskName = null; + if (Logger.DEBUG_TASKSJOB) { + taskName = SSECoreMessages.TaskScanningJob_0 + " (" + ticks + " work items)"; //$NON-NLS-1$ //$NON-NLS-2$ + } + else { + taskName = SSECoreMessages.TaskScanningJob_0; + } + monitor.beginTask(taskName, ticks); + + IProgressMonitor scanMonitor = null; + while (!currentQueue.isEmpty()) { + Object o = currentQueue.remove(0); + if (frameworkIsShuttingDown() || monitor.isCanceled()) + return Status.CANCEL_STATUS; + try { + scanMonitor = new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + if (o instanceof IResourceDelta) { + WorkspaceTaskScanner.getInstance().scan((IResourceDelta) o, scanMonitor); + } + else if (o instanceof IProject) { + WorkspaceTaskScanner.getInstance().scan((IProject) o, scanMonitor); + if(!scanMonitor.isCanceled() ) { + String[] projectsPreviouslyScanned = StringUtils.unpack(SSECorePlugin.getDefault().getPluginPreferences().getString(TASK_TAG_PROJECTS_ALREADY_SCANNED)); + String[] updatedProjects = new String[projectsPreviouslyScanned.length + 1]; + updatedProjects[projectsPreviouslyScanned.length] = ((IResource) o).getName(); + System.arraycopy(projectsPreviouslyScanned, 0, updatedProjects, 0, projectsPreviouslyScanned.length); + SSECorePlugin.getDefault().getPluginPreferences().setValue(TASK_TAG_PROJECTS_ALREADY_SCANNED, StringUtils.pack(updatedProjects)); + } + } + } + catch (Exception e) { + if (errors == null) { + errors = new ArrayList(); + } + errors.add(new Status(IStatus.ERROR, SSECorePlugin.ID, IStatus.ERROR, "", e)); //$NON-NLS-1$ + } + } + monitor.done(); + + if (errors == null || errors.isEmpty()) { + status = Status.OK_STATUS; + } + else { + if (errors.size() == 1) { + status = (IStatus) errors.get(0); + } + else { + IStatus[] statii = (IStatus[]) errors.toArray(new IStatus[errors.size()]); + status = new MultiStatus(SSECorePlugin.ID, IStatus.ERROR, statii, SSECoreMessages.TaskScanningJob_1, null); + } + } + + SSECorePlugin.getDefault().savePluginPreferences(); + return status; + } + + private void cleanupRememberedProjectList(String preferenceName) { + String[] rememberedProjectNames = StringUtils.unpack(SSECorePlugin.getDefault().getPluginPreferences().getString(preferenceName)); + IResource[] workspaceProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + String[] projectNames = new String[workspaceProjects.length]; + for (int i = 0; i < projectNames.length; i++) { + projectNames[i] = workspaceProjects[i].getName(); + } + + List projectNamesToRemember = new ArrayList(rememberedProjectNames.length); + for (int i = 0; i < rememberedProjectNames.length; i++) { + boolean rememberedProjectExists = false; + for (int j = 0; !rememberedProjectExists && j < projectNames.length; j++) { + if (rememberedProjectNames[i].equals(projectNames[j])) { + rememberedProjectExists = true; + } + } + if (rememberedProjectExists) { + projectNamesToRemember.add(rememberedProjectNames[i]); + } + else if (Logger.DEBUG_TASKSJOB) { + System.out.println("Removing " + rememberedProjectNames[i] + " removed from " + preferenceName); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + if (projectNamesToRemember.size() != rememberedProjectNames.length) { + SSECorePlugin.getDefault().getPluginPreferences().setValue(preferenceName, StringUtils.pack((String[]) projectNamesToRemember.toArray(new String[projectNamesToRemember.size()]))); + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskScanningScheduler.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskScanningScheduler.java new file mode 100644 index 0000000..6095a6d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskScanningScheduler.java
@@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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.wst.sse.core.internal.tasks; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.utils.StringUtils; + +public class TaskScanningScheduler { + class ListenerVisitor implements IResourceChangeListener, IResourceDeltaVisitor { + public void resourceChanged(IResourceChangeEvent event) { + IResourceDelta delta = event.getDelta(); + if (delta.getResource() != null) { + int resourceType = delta.getResource().getType(); + if (resourceType == IResource.PROJECT || resourceType == IResource.ROOT) { + try { + delta.accept(this); + } + catch (CoreException e) { + Logger.logException("Exception handling resource change", e); //$NON-NLS-1$ + } + } + } + } + + public boolean visit(IResourceDelta delta) throws CoreException { + if ((delta.getKind() & IResourceDelta.MARKERS) > 0 || (delta.getKind() & IResourceDelta.ENCODING) > 0 || (delta.getKind() & IResourceDelta.NO_CHANGE) > 0) + return false; + + IResource resource = delta.getResource(); + if (resource != null) { + if (resource.getType() == IResource.ROOT) + return true; + else if (resource.getType() == IResource.PROJECT) { + fJob.addProjectDelta(delta); + return false; + } + } + return false; + } + + } + + private static TaskScanningScheduler scheduler; + + public static void refresh() { + SSECorePlugin.getDefault().getPluginPreferences().setValue(TaskScanningJob.TASK_TAG_PROJECTS_ALREADY_SCANNED, ""); //$NON-NLS-1$ + scheduler.enqueue(ResourcesPlugin.getWorkspace().getRoot()); + } + + public static void refresh(IProject project) { + String[] projectNames = StringUtils.unpack(SSECorePlugin.getDefault().getPluginPreferences().getString(TaskScanningJob.TASK_TAG_PROJECTS_ALREADY_SCANNED)); //$NON-NLS-1$ + List freshProjectList = new ArrayList(); + for (int i = 0; i < projectNames.length; i++) { + if (!projectNames[i].equals(project.getName())) { + freshProjectList.add(projectNames[i]); + } + } + String freshProjects = StringUtils.pack((String[]) freshProjectList.toArray(new String[freshProjectList.size()])); + SSECorePlugin.getDefault().getPluginPreferences().setValue(TaskScanningJob.TASK_TAG_PROJECTS_ALREADY_SCANNED, freshProjects); //$NON-NLS-1$ + + scheduler.enqueue(project); + } + + + /** + * Only for use by SSECorePlugin class + */ + public static void shutdown() { + if (scheduler != null) { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(scheduler.visitor); + scheduler.fJob.cancel(); + try { + scheduler.fJob.join(); + } + catch (InterruptedException e) { + Logger.logException(e); + } + } + } + + /** + * Only for use by SSEUIPlugin class, UI by nature of its output being meant for the user + */ + public static void startup() { + scheduler = new TaskScanningScheduler(); + + /* + * According to + * http://www.eclipse.org/eclipse/development/performance/bloopers.html, + * POST_CHANGE listeners add a trivial performance cost. + * + * Always add listener since we might be enabled per-project even if disabled workspace-wide. + */ + ResourcesPlugin.getWorkspace().addResourceChangeListener(scheduler.visitor, IResourceChangeEvent.POST_CHANGE); + + scheduler.enqueue(ResourcesPlugin.getWorkspace().getRoot()); + } + + TaskScanningJob fJob = null; + + ListenerVisitor visitor = null; + + private TaskScanningScheduler() { + super(); + fJob = new TaskScanningJob(); + visitor = new ListenerVisitor(); + } + + void enqueue(IProject project) { + fJob.addProject(project); + } + + void enqueue(IWorkspaceRoot root) { + IProject[] allProjects = root.getProjects(); + for (int i = 0; i < allProjects.length; i++) { + fJob.addProject(allProjects[i]); + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskTagPreferenceKeys.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskTagPreferenceKeys.java new file mode 100644 index 0000000..bb3e305 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/TaskTagPreferenceKeys.java
@@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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.wst.sse.core.internal.tasks; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; + +public final class TaskTagPreferenceKeys { + public static final String TASK_TAG_CONTENTTYPES_IGNORED = "ignored-contentTypes"; //$NON-NLS-1$ + public static final String TASK_TAG_ENABLE = "enabled"; //$NON-NLS-1$ + public static final String TASK_TAG_NODE = SSECorePlugin.ID + IPath.SEPARATOR + "task-tags"; //$NON-NLS-1$ + public static final String TASK_TAG_PER_PROJECT = "use-project-settings"; //$NON-NLS-1$ + public static final String TASK_TAG_PRIORITIES = "taskPriorities"; //$NON-NLS-1$ + public static final String TASK_TAG_TAGS = "taskTags"; //$NON-NLS-1$ +}
diff --git a/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/WorkspaceTaskScanner.java b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/WorkspaceTaskScanner.java new file mode 100644 index 0000000..3f3ffa6 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src-tasktags/org/eclipse/wst/sse/core/internal/tasks/WorkspaceTaskScanner.java
@@ -0,0 +1,467 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300443 - some constants aren't static final + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.tasks; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IPreferencesService; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.tasks.IFileTaskScanner; +import org.eclipse.wst.sse.core.internal.provisional.tasks.TaskTag; +import org.eclipse.wst.sse.core.utils.StringUtils; + +/** + * Dispatcher for scanning based on deltas and requested projects + */ +class WorkspaceTaskScanner { + private static WorkspaceTaskScanner _instance = null; + static final String SYNTHETIC_TASK = "org.eclipse.wst.sse.task-synthetic"; + static final String MODIFICATION_STAMP = "org.eclipse.wst.sse.modification-stamp"; + private boolean proceed = false; + + static synchronized WorkspaceTaskScanner getInstance() { + if (_instance == null) { + _instance = new WorkspaceTaskScanner(); + } + return _instance; + } + + static final String DEFAULT_MARKER_TYPE = IFileTaskScanner.TASK_MARKER_ID; + private List fActiveScanners = null; + private IContentType[] fCurrentIgnoreContentTypes = null; + private TaskTag[] fCurrentTaskTags = null; + + private FileTaskScannerRegistryReader registry = null; + + private long time0; + + /** + * + */ + private WorkspaceTaskScanner() { + super(); + registry = FileTaskScannerRegistryReader.getInstance(); + fActiveScanners = new ArrayList(); + fCurrentTaskTags = new TaskTag[0]; + fCurrentIgnoreContentTypes = new IContentType[0]; + } + + private IContentType[] detectContentTypes(IResource resource) { + IContentType[] types = null; + if (resource.getType() == IResource.FILE && resource.isAccessible()) { + types = Platform.getContentTypeManager().findContentTypesFor(resource.getName()); + if (types.length == 0) { + IContentDescription d = null; + try { + // optimized description lookup, might not succeed + d = ((IFile) resource).getContentDescription(); + if (d != null) { + types = new IContentType[]{d.getContentType()}; + } + } + catch (CoreException e) { + /* + * should not be possible given the accessible and file + * type check above + */ + } + } + if (types == null) { + types = Platform.getContentTypeManager().findContentTypesFor(resource.getName()); + } + if (Logger.DEBUG_TASKSCONTENTTYPE) { + if (types.length > 0) { + if (types.length > 1) { + System.out.println(resource.getFullPath() + ": " + "multiple based on name (probably hierarchical)"); //$NON-NLS-1$ //$NON-NLS-2$ + } + for (int i = 0; i < types.length; i++) { + System.out.println(resource.getFullPath() + " matched: " + types[i].getId()); //$NON-NLS-1$ + } + } + } + } + return types; + } + + /** + * @param resource + * @return + */ + private IProject getProject(IResource resource) { + IProject project = null; + if (resource.getType() == IResource.PROJECT) { + project = (IProject) resource; + } + else { + project = resource.getProject(); + } + return project; + } + + private boolean init(IResource resource) { + IProject project = getProject(resource); + + IPreferencesService preferencesService = Platform.getPreferencesService(); + IScopeContext[] lookupOrder = new IScopeContext[]{new ProjectScope(project), new InstanceScope(), new DefaultScope()}; + + boolean proceed = preferencesService.getBoolean(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_ENABLE, false, lookupOrder); + + if (Logger.DEBUG_TASKSPREFS) { + System.out.println(getClass().getName() + " scan of " + resource.getFullPath() + ":" + proceed); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (proceed) { + String[] tags = StringUtils.unpack(preferencesService.getString(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_TAGS, null, lookupOrder)); + String[] priorities = StringUtils.unpack(preferencesService.getString(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_PRIORITIES, null, lookupOrder)); + String[] currentIgnoreContentTypeIDs = StringUtils.unpack(preferencesService.getString(TaskTagPreferenceKeys.TASK_TAG_NODE, TaskTagPreferenceKeys.TASK_TAG_CONTENTTYPES_IGNORED, null, lookupOrder)); + if (Logger.DEBUG_TASKSPREFS) { + System.out.print(getClass().getName() + " tags: "); //$NON-NLS-1$ + for (int i = 0; i < tags.length; i++) { + if (i > 0) { + System.out.print(","); //$NON-NLS-1$ + } + System.out.print(tags[i]); + } + System.out.println(); + System.out.print(getClass().getName() + " priorities: "); //$NON-NLS-1$ + for (int i = 0; i < priorities.length; i++) { + if (i > 0) { + System.out.print(","); //$NON-NLS-1$ + } + System.out.print(priorities[i]); + } + System.out.println(); + System.out.print(getClass().getName() + " ignored content types: "); //$NON-NLS-1$ + for (int i = 0; i < currentIgnoreContentTypeIDs.length; i++) { + if (i > 0) { + System.out.print(","); //$NON-NLS-1$ + } + System.out.print(currentIgnoreContentTypeIDs[i]); + } + System.out.println(); + } + fCurrentIgnoreContentTypes = new IContentType[currentIgnoreContentTypeIDs.length]; + IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); + for (int i = 0; i < currentIgnoreContentTypeIDs.length; i++) { + fCurrentIgnoreContentTypes[i] = contentTypeManager.getContentType(currentIgnoreContentTypeIDs[i]); + } + int max = Math.min(tags.length, priorities.length); + fCurrentTaskTags = new TaskTag[max]; + for (int i = 0; i < max; i++) { + int priority = TaskTag.PRIORITY_NORMAL; + try { + priority = Integer.parseInt(priorities[i]); + } + catch (NumberFormatException e) { + // default to normal priority + } + fCurrentTaskTags[i] = new TaskTag(tags[i], priority); + } + } + return proceed; + } + + void internalScan(final IProject project, final IResource resource, final IProgressMonitor scanMonitor) { + if (scanMonitor.isCanceled()) + return; + try { + String name = resource.getName(); + if (resource.isAccessible() && !resource.isDerived() && !resource.isPhantom() && !resource.isTeamPrivateMember() && name.length() != 0 && name.charAt(0) != '.') { + if ((resource.getType() & IResource.FOLDER) > 0 || (resource.getType() & IResource.PROJECT) > 0) { + IResource[] children = ((IContainer) resource).members(); + scanMonitor.beginTask("", children.length); //$NON-NLS-1$ + for (int i = 0; i < children.length; i++) { + internalScan(project, children[i], new SubProgressMonitor(scanMonitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); + } + scanMonitor.done(); + } + else if ((resource.getType() & IResource.FILE) > 0) { + scanFile(project, fCurrentTaskTags, (IFile) resource, scanMonitor); + } + } + } + catch (CoreException e) { + Logger.logException(e); + } + } + + void internalScan(IResourceDelta delta, final IProgressMonitor monitor) { + if (monitor.isCanceled()) + return; + try { + String name = delta.getFullPath().lastSegment(); + IResource resource = delta.getResource(); + if (!resource.isDerived() && !resource.isPhantom() && !resource.isTeamPrivateMember() && name.length() != 0 && name.charAt(0) != '.') { + if ((resource.getType() & IResource.FOLDER) > 0 || (resource.getType() & IResource.PROJECT) > 0) { + IResourceDelta[] children = delta.getAffectedChildren(); + monitor.beginTask("", children.length); + if (name.length() != 0 && name.charAt(0) != '.' && children.length > 0) { + for (int i = children.length - 1; i >= 0; i--) { + internalScan(children[i], new SubProgressMonitor(monitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); + } + } + monitor.done(); + } + else if ((resource.getType() & IResource.FILE) > 0) { + if ((delta.getKind() & IResourceDelta.ADDED) > 0 || ((delta.getKind() & IResourceDelta.CHANGED) > 0 && (delta.getFlags() & IResourceDelta.CONTENT) > 0)) { + IFile file = (IFile) resource; + scanFile(file.getProject(), fCurrentTaskTags, file, monitor); + } + } + } + } + catch (Exception e) { + Logger.logException(e); + } + } + + private void replaceTaskMarkers(final IFile file, final String[] markerTypes, final Map markerAttributeMaps[], IProgressMonitor monitor) { + final IFile finalFile = file; + if (file.isAccessible()) { + try { + IWorkspaceRunnable r = new IWorkspaceRunnable() { + public void run(IProgressMonitor progressMonitor) throws CoreException { + progressMonitor.beginTask("", 2);//$NON-NLS-1$ + try { + /* + * Delete old Task markers (don't delete regular + * Tasks since that includes user-defined ones) + */ + for (int i = 0; i < markerTypes.length; i++) { + if (IMarker.TASK.equals(markerTypes[i])) { + // only remove if synthetic + IMarker[] foundMarkers = file.findMarkers(markerTypes[i], true, IResource.DEPTH_ZERO); + for (int j = 0; j < foundMarkers.length; j++) { + if (foundMarkers[j].getAttribute(SYNTHETIC_TASK) != null) { + foundMarkers[j].delete(); + } + } + } + else { + file.deleteMarkers(markerTypes[i], true, IResource.DEPTH_ZERO); + } + } + } + catch (CoreException e) { + Logger.logException("exception deleting old tasks", e); //$NON-NLS-1$ + } + finally { + progressMonitor.worked(1); + } + if (proceed && markerAttributeMaps != null && markerAttributeMaps.length > 0) { + if (Logger.DEBUG_TASKS) { + System.out.println("" + markerAttributeMaps.length + " tasks for " + file.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ + } + for (int i = 0; i < markerAttributeMaps.length; i++) { + String specifiedMarkerType = (String) markerAttributeMaps[i].get(IMarker.TASK); + IMarker marker = finalFile.createMarker(specifiedMarkerType); + marker.setAttributes(markerAttributeMaps[i]); + marker.setAttribute(IMarker.USER_EDITABLE, Boolean.FALSE); + marker.setAttribute(MODIFICATION_STAMP, Long.toString(file.getModificationStamp())); + if (IMarker.TASK.equals(specifiedMarkerType)) { + // set to synthetic and make user editable + marker.setAttribute(SYNTHETIC_TASK, true); + } + } + } + progressMonitor.worked(1); + progressMonitor.done(); + } + }; + if (file.isAccessible()) { + finalFile.getWorkspace().run(r, ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(file), IWorkspace.AVOID_UPDATE, monitor); + } + } + catch (CoreException e1) { + Logger.logException(e1); + } + catch(OperationCanceledException e) { + // not an error condition + } + } + } + + void scan(final IProject project, final IProgressMonitor scanMonitor) { + if (scanMonitor.isCanceled()) + return; + if (Logger.DEBUG_TASKS) { + System.out.println(getClass().getName() + " scanning project " + project.getName()); //$NON-NLS-1$ + } + if (!project.isAccessible()) { + if (Logger.DEBUG_TASKS) { + System.out.println(getClass().getName() + " skipping inaccessible project " + project.getName()); //$NON-NLS-1$ + } + return; + } + + if (Logger.DEBUG_TASKSOVERALLPERF) { + time0 = System.currentTimeMillis(); + } + proceed = init(project); + internalScan(project, project, scanMonitor); + shutdownDelegates(project); + + if (Logger.DEBUG_TASKSOVERALLPERF) { + System.out.println("" + (System.currentTimeMillis() - time0) + "ms for " + project.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + + void scan(IResourceDelta delta, final IProgressMonitor monitor) { + if (monitor.isCanceled()) + return; + if (Logger.DEBUG_TASKSOVERALLPERF) { + time0 = System.currentTimeMillis(); + } + if (init(delta.getResource())) { + internalScan(delta, monitor); + shutdownDelegates(delta.getResource().getProject()); + } + if (Logger.DEBUG_TASKSOVERALLPERF) { + System.out.println("" + (System.currentTimeMillis() - time0) + "ms for " + delta.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + void scanFile(IProject project, TaskTag[] taskTags, IFile file, IProgressMonitor monitor) { + if (monitor.isCanceled()) + return; + + // 3 "stages" + monitor.beginTask("", 8);//$NON-NLS-1$ + monitor.subTask(file.getFullPath().toString().substring(1)); + + final List markerAttributes = new ArrayList(); + IContentType[] types = detectContentTypes(file); + Set markerTypes = new HashSet(3); + // Always included for safety and migration + markerTypes.add(DEFAULT_MARKER_TYPE); + monitor.worked(1); + + IFileTaskScanner[] fileScanners = null; + IFileTaskScanner[] ignoredFileScanners = null; + if (types != null) { + if (fCurrentIgnoreContentTypes.length == 0) { + fileScanners = registry.getFileTaskScanners(types); + } + else { + List validTypes = new ArrayList(); + // obtain a filtered list of delegates + for (int i = 0; i < types.length; i++) { + boolean ignoreContentType = false; + for (int j = 0; j < fCurrentIgnoreContentTypes.length; j++) { + ignoreContentType = ignoreContentType || types[i].isKindOf(fCurrentIgnoreContentTypes[j]); + } + if (!ignoreContentType) { + validTypes.add(types[i]); + } + else { + ignoredFileScanners = registry.getFileTaskScanners(new IContentType[] {types[i]}); + } + } + fileScanners = registry.getFileTaskScanners((IContentType[]) validTypes.toArray(new IContentType[validTypes.size()])); + } + monitor.worked(1); + if (ignoredFileScanners != null && ignoredFileScanners.length > 0) { + for (int i = 0; i < ignoredFileScanners.length; i++) + markerTypes.add(ignoredFileScanners[i].getMarkerType()); + } + + if (fileScanners.length > 0) { + IProgressMonitor scannerMonitor = new SubProgressMonitor(monitor, 3, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + scannerMonitor.beginTask("", fileScanners.length); //$NON-NLS-1$ + for (int j = 0; fileScanners != null && j < fileScanners.length; j++) { + if (monitor.isCanceled()) + continue; + try { + if (!fActiveScanners.contains(fileScanners[j]) && !monitor.isCanceled()) { + fileScanners[j].startup(file.getProject()); + fActiveScanners.add(fileScanners[j]); + } + markerTypes.add(fileScanners[j].getMarkerType()); + Map[] taskMarkerAttributes = fileScanners[j].scan(file, taskTags, new SubProgressMonitor(scannerMonitor, 1, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); + /* + * TODO: pool the marker results so there's only one + * operation creating them + */ + for (int i = 0; i < taskMarkerAttributes.length; i++) { + if (!taskMarkerAttributes[i].containsKey(IMarker.TASK)) { + taskMarkerAttributes[i].put(IMarker.TASK, fileScanners[j].getMarkerType()); + } + taskMarkerAttributes[i].put(IMarker.SOURCE_ID, fileScanners[j].getClass().getName()); + markerAttributes.add(taskMarkerAttributes[i]); + } + } + catch (Exception e) { + Logger.logException(file.getFullPath().toString(), e); + } + } + scannerMonitor.done(); + } + } + else { + monitor.worked(4); + } + + if (monitor.isCanceled()) + return; + // only update markers if we ran a scanner on this file + if (fileScanners != null && fileScanners.length > 0 || + ignoredFileScanners != null && ignoredFileScanners.length > 0) { + IProgressMonitor markerUpdateMonitor = new SubProgressMonitor(monitor, 3, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + if (markerAttributes != null) + replaceTaskMarkers(file, (String[]) markerTypes.toArray(new String[markerTypes.size()]), (Map[]) markerAttributes.toArray(new Map[markerAttributes.size()]), markerUpdateMonitor); + } + else { + monitor.worked(3); + } + monitor.done(); + } + + private void shutdownDelegates(IProject project) { + for (int j = 0; j < fActiveScanners.size(); j++) { + try { + ((IFileTaskScanner) fActiveScanners.get(j)).shutdown(project); + } + catch (Exception e) { + Logger.logException(project.getFullPath().toString(), e); + } + } + fActiveScanners = new ArrayList(1); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/StructuredModelManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/StructuredModelManager.java new file mode 100644 index 0000000..ecb0ece --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/StructuredModelManager.java
@@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2004, 2011 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.wst.sse.core; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.internal.model.ModelManagerImpl; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.osgi.framework.Bundle; + +/** + * Class to allow access to properly configured implementors of IModelManager. + * + * @since 1.5 org.eclipse.wst.sse.core + */ +final public class StructuredModelManager { + /** + * Do not allow instances to be created. + */ + private StructuredModelManager() { + super(); + } + + /** + * Provides access to the instance of IModelManager. Returns null if model + * manager can not be created or is not valid (such as, when workbench is + * shutting down). + * + * @return IModelManager - returns the one model manager for structured + * models or null if the owning bundle is neither active nor + * starting. + */ + public static IModelManager getModelManager() { + boolean isReady = false; + IModelManager modelManager = null; + boolean interrupted = false; + try { + while (!isReady) { + Bundle localBundle = Platform.getBundle(SSECorePlugin.ID); + int state = localBundle.getState(); + if (state == Bundle.ACTIVE) { + isReady = true; + // getInstance is a synchronized static method. + modelManager = ModelManagerImpl.getInstance(); + } + else if (state == Bundle.STARTING) { + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + // ignore, just loop again + interrupted = true; + } + } + else if (state == Bundle.STOPPING || state == Bundle.UNINSTALLED) { + isReady = true; + modelManager = null; + } + else { + // not sure about other states, 'resolved', 'installed' + isReady = true; + modelManager = null; + } + } + } + finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + return modelManager; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java new file mode 100644 index 0000000..f553b58 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java
@@ -0,0 +1,1990 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 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.wst.sse.core.indexing; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IResourceProxy; +import org.eclipse.core.resources.IResourceProxyVisitor; +import org.eclipse.core.resources.ISaveParticipant; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.osgi.util.NLS; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; + +/** + * <p> + * A generic class for implementing a resource index manager. It is important + * to note that this only provides the framework for managing an index, not + * actually indexing. The subtle difference is that the manager is in charge + * of paying attention to all of the resource actions that take place in the + * workspace and filtering those actions down to simple actions that need to + * be performed on whatever index this manager is managing. + * </p> + * + * <p> + * The manager does its very best to make sure the index is always consistent, + * even if resource events take place when the manager is not running. In the + * event that the manager determines it has missed, lost, or corrupted any + * resource change events that have occurred before, during, or after its + * activation or deactivation then the manager will inspect the entire + * workspace to insure the index it is managing is consistent. + * </p> + * + */ +public abstract class AbstractIndexManager { + + /** Used to encode path bytes in case they contain double byte characters */ + private static final String ENCODING_UTF16 = "utf16"; //$NON-NLS-1$ + + /** Default time to wait for other tasks to finish */ + private static final int WAIT_TIME = 300; + + /** + * <p> + * Used to report progress on jobs where the total work to complete is + * unknown. The created effect is a progress bar that moves but that will + * never complete. + * </p> + */ + private static final int UNKNOWN_WORK = 100; + + /** + * The amount of events to batch up before sending them off to the + * processing job + */ + private static final int BATCH_UP_AMOUNT = 100; + + /** If this file exists then a full workspace re-processing is needed */ + private static final String RE_PROCESS_FILE_NAME = ".re-process"; //$NON-NLS-1$ + + /** Common error message to log */ + private static final String LOG_ERROR_INDEX_INVALID = "Index may become invalid, incomplete, or enter some other inconsistent state."; //$NON-NLS-1$ + + /** State: manager is stopped */ + private static final byte STATE_DISABLED = 0; + + /** State: manager is running */ + private static final byte STATE_ENABLED = 1; + + /** Action: add to index */ + protected static final byte ACTION_ADD = 0; + + /** Action: remove from index */ + protected static final byte ACTION_REMOVE = 1; + + /** Action: add to index caused by move operation */ + protected static final byte ACTION_ADD_MOVE_FROM = 2; + + /** Action: remove from index caused by move operation */ + protected static final byte ACTION_REMOVE_MOVE_TO = 3; + + /** Source: action originated from resource change event */ + protected static final byte SOURCE_RESOURCE_CHANGE = 0; + + /** Source: action originated from workspace scan */ + protected static final byte SOURCE_WORKSPACE_SCAN = 1; + + /** Source: action originated from saved state */ + protected static final byte SOURCE_SAVED_STATE = 2; + + /** Source: preserved resources to index */ + protected static final byte SOURCE_PRESERVED_RESOURCES_TO_INDEX = 3; + + /** the name of this index manager */ + private String fName; + + /** {@link IResourceChangeListener} to listen for file changes */ + private ResourceChangeListener fResourceChangeListener; + + /** The {@link Job} that does all of the indexing */ + private ResourceEventProcessingJob fResourceEventProcessingJob; + + /** A {@link Job} to search the workspace for all files */ + private Job fWorkspaceVisitorJob; + + /** + * <p> + * Current state of the manager + * </p> + * + * @see #STATE_DISABLED + * @see #STATE_ENABLED + */ + private volatile byte fState; + + /** used to prevent manager from starting and stopping at the same time */ + private Object fStartStopLock = new Object(); + + /** + * <code>true</code> if the manager is currently starting, + * <code>false</code> otherwise + */ + private boolean fStarting; + + /** + * <p> + * Creates the manager with a given name. + * </p> + * + * @param name + * This will be pre-pended to progress reporting messages and + * thus should be translated + */ + protected AbstractIndexManager(String name) { + this.fName = name; + this.fState = STATE_DISABLED; + this.fResourceChangeListener = new ResourceChangeListener(); + this.fResourceEventProcessingJob = new ResourceEventProcessingJob(); + this.fStarting = false; + } + + /** + * <p> + * Creates the manager with a given name. + * </p> + * + * @param name + * This will be pre-pended to progress reporting messages and + * thus should be translated + * + * @param messageRunning + * ignored + * @param messageInitializing + * ignored + * @param messageProcessingFiles + * ignored + * + * @deprecated This constructor ignores the last three parameters. + * @see #AbstractIndexManager(String) + */ + + protected AbstractIndexManager(String name, String messageRunning, String messageInitializing, String messageProcessingFiles) { + this(name); + } + + /** + * <p> + * Starts up the {@link AbstractIndexManager}. If a {@link IResourceDelta} + * is provided then it is assumed that all other files in the workspace + * have already been index and thus only those in the provided + * {@link IResourceDelta} will be processed. Else if the provided + * {@link IResourceDelta} is <code>null</code> it is assumed no files have + * been indexed yet so the entire workspace will be searched for files to + * be indexed. + * </p> + * + * <p> + * If {@link IResourceDelta} is provided this will block until that delta + * has finished processing. If no {@link IResourceDelta} provided then a + * separate job will be created to process the entire workspace and this + * method will return without waiting for that job to complete + * </p> + * + * <p> + * Will block until {@link #stop()} has finished running if it is + * currently running + * </p> + * + * @param savedStateDelta + * the delta from a saved state, if <code>null</code> then the + * entire workspace will be searched for files to index, else + * only files in this {@link IResourceDelta} will be indexed + * @param monitor + * This action can not be canceled but this monitor will be + * used to report progress + */ + public final void start(IResourceDelta savedStateDelta, IProgressMonitor monitor) { + SubMonitor progress = SubMonitor.convert(monitor); + synchronized (this.fStartStopLock) { + this.fStarting = true; + + try { + if (this.fState == STATE_DISABLED) { + // report status + progress.beginTask(NLS.bind(SSECoreMessages.IndexManager_0_starting, this.fName), 2); + + // start listening for resource change events + this.fResourceChangeListener.start(); + + // check to see if a full re-index is required + boolean forcedFullReIndexNeeded = this.isForcedFullReIndexNeeded(); + + /* + * start the indexing job only loading preserved state if + * not doing full index if failed loading preserved state + * then force full re-index + */ + forcedFullReIndexNeeded = !this.fResourceEventProcessingJob.start(!forcedFullReIndexNeeded, progress.newChild(1)); + progress.setWorkRemaining(1); + + // don't bother processing saved delta if forced full + // re-index is needed + if (!forcedFullReIndexNeeded) { + /* + * if there is a delta attempt to process it else need + * to do a full workspace index + */ + if (savedStateDelta != null) { + forcedFullReIndexNeeded = false; + try { + // deal with reporting progress + SubMonitor savedStateProgress = progress.newChild(1, SubMonitor.SUPPRESS_NONE); + + savedStateProgress.setTaskName(NLS.bind(SSECoreMessages.IndexManager_0_starting_1, new String[]{this.fName, SSECoreMessages.IndexManager_processing_deferred_resource_changes})); + + // process delta + ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(savedStateProgress, AbstractIndexManager.SOURCE_SAVED_STATE); + savedStateDelta.accept(visitor); + + // process any remaining batched up resources + // to index + visitor.processBatchedResourceEvents(); + } + catch (CoreException e) { + forcedFullReIndexNeeded = true; + Logger.logException(this.fName + ": Could not process saved state. " + //$NON-NLS-1$ + "Forced to do a full workspace re-index.", e); //$NON-NLS-1$ + } + } + else { + forcedFullReIndexNeeded = true; + } + } + progress.worked(1); + + // if need to process the entire workspace do so in + // another job + if (forcedFullReIndexNeeded) { + this.fWorkspaceVisitorJob = new WorkspaceVisitorJob(); + this.fWorkspaceVisitorJob.schedule(); + } + + // update state + this.fState = STATE_ENABLED; + } + } + finally { + this.fStarting = false; + progress.done(); + } + } + } + + /** + * <p> + * Safely shuts down the manager. + * </p> + * + * <p> + * This will block until the + * {@link #start(IResourceDelta, IProgressMonitor)} has finished (if + * running). Also until the current resource event has finished being + * processed. Finally it will block until the events still to be processed + * by the processing job have been preserved to be processed on the next + * call to {@link #start(IResourceDelta, IProgressMonitor)}. + * </p> + * + * <p> + * If at any point during this shut down processes something goes wrong + * the manager will be sure that on the next call to + * {@link #start(IResourceDelta, IProgressMonitor)} the entire workspace + * will be re-processed. + * </p> + * + * @throws InterruptedException + */ + public final void stop() throws InterruptedException { + synchronized (this.fStartStopLock) { + if (this.fState != STATE_DISABLED) { + + // stop listening for events, and wait for the current event + // to finish + this.fResourceChangeListener.stop(); + + // if currently visiting entire workspace, give up and try + // again next load + boolean forceFullReIndexNextStart = false; + if (this.fWorkspaceVisitorJob != null) { + if (this.fWorkspaceVisitorJob.getState() != Job.NONE) { + this.fWorkspaceVisitorJob.cancel(); + + this.forceFullReIndexNextStart(); + forceFullReIndexNextStart = true; + } + } + + // stop the indexing job, only preserve if not already forcing + // a re-index + forceFullReIndexNextStart = !this.fResourceEventProcessingJob.stop(!forceFullReIndexNextStart); + + // if preserving failed, then force re-index + if (forceFullReIndexNextStart) { + this.forceFullReIndexNextStart(); + } + + // update status + this.fState = STATE_DISABLED; + } + } + } + + /** + * @return the name of this indexer + */ + protected String getName() { + return this.fName; + } + + /** + * <p> + * Should be called by a client of the index this manager manages before + * the index is accessed, assuming the client wants an index consistent + * with the latest resource changes. + * </p> + * + * <p> + * The supplied monitor will be used to supply user readable progress as + * the manager insures the index has been given all the latest resource + * events. This monitor may be canceled, but if it is the state of the + * index is not guaranteed to be consistent with the latest resource + * change events. + * </p> + * + * @param monitor + * Used to report user readable progress as the manager insures + * the index is consistent with the latest resource events. + * This monitor can be canceled to stop waiting for consistency + * but then no guaranty is made about the consistency of the + * index in relation to unprocessed resource changes + * + * @return <code>true</code> if the wait finished successfully and the + * manager is consistent, <code>false</code> otherwise, either an + * error occurred while waiting for the manager or the monitor was + * canceled + * + * @throws InterruptedException + * This can happen when waiting for other jobs + */ + public final boolean waitForConsistent(IProgressMonitor monitor) { + boolean success = true; + boolean interupted = false; + SubMonitor progress = SubMonitor.convert(monitor); + + // set up the progress of waiting + int remainingWork = 4; + progress.beginTask(NLS.bind(SSECoreMessages.IndexManager_Waiting_for_0, this.fName), remainingWork); + + // wait for start up + if (this.fStarting && !monitor.isCanceled()) { + SubMonitor startingProgress = progress.newChild(1); + startingProgress.subTask(NLS.bind(SSECoreMessages.IndexManager_0_starting, this.fName)); + while (this.fStarting && !monitor.isCanceled()) { + // this creates a never ending progress that still moves + // forward + startingProgress.setWorkRemaining(UNKNOWN_WORK); + startingProgress.newChild(1).worked(1); + try { + Thread.sleep(WAIT_TIME); + } + catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + // wait for workspace visiting job + if (this.fWorkspaceVisitorJob != null && this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) { + SubMonitor workspaceVisitorProgress = progress.newChild(1); + workspaceVisitorProgress.subTask(SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time); + while (this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) { + // this creates a never ending progress that still moves + // forward + workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK); + workspaceVisitorProgress.newChild(1).worked(1); + try { + Thread.sleep(WAIT_TIME); + } + catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + // wait for the current resource event + if (this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) { + SubMonitor workspaceVisitorProgress = progress.newChild(1); + workspaceVisitorProgress.subTask(SSECoreMessages.IndexManager_processing_recent_resource_changes); + while (this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) { + workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK); + workspaceVisitorProgress.newChild(1).worked(1); + try { + this.fResourceChangeListener.waitForCurrentEvent(WAIT_TIME); + } + catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + // wait for all files to be indexed + if (this.fResourceEventProcessingJob.getNumResourceEventsToProcess() != 0 && !monitor.isCanceled()) { + SubMonitor indexingProgress = progress.newChild(1); + int prevNumResources; + int numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess(); + while (numResources != 0 && !monitor.isCanceled()) { + // update the progress indicator + indexingProgress.subTask(NLS.bind(SSECoreMessages.IndexManager_0_Indexing_1_Files, new Object[]{AbstractIndexManager.this.fName, "" + numResources})); //$NON-NLS-1$ + indexingProgress.setWorkRemaining(numResources); + prevNumResources = numResources; + numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess(); + int numProcessed = prevNumResources - numResources; + indexingProgress.worked(numProcessed > 0 ? numProcessed : 0); + + // give the index some time to do some indexing + try { + this.fResourceEventProcessingJob.waitForConsistant(WAIT_TIME); + } + catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + if (monitor.isCanceled()) { + success = false; + } + + // reset the interrupted flag if we were interrupted + if (interupted) { + Thread.currentThread().interrupt(); + } + + progress.done(); + return success; + } + + /** + * <p> + * Called for each {@link IResource} given in a resource delta. If the + * resource type is a file then used to determine if that file should be + * processed by the manager, if the resource type is a project or + * directory then it is used to determine if the children of the project + * or directory should be processed looking for file resources. + * </p> + * + * <p> + * <b>NOTE:</b> Even if <code>true</code> is returned for a directory + * resource that only means the children of the directory should be + * inspected for possible files to index. Directories themselves can not + * be managed in order to add to an index. + * </p> + * + * @param type + * the {@link IResource#getType()} result of the resource to + * possibly index + * @param path + * the full {@link IPath} to the resource to possibly index + * @return <code>true</code> If the resource with the given + * <code>type</code> and <code>path</code> should either itself be + * indexed, or its children should be indexed, <code>false</code> + * if neither the described resource or any children should be + * indexed + */ + protected abstract boolean isResourceToIndex(int type, IPath path); + + /** + * <p> + * Called for each {@link ResourceEvent} gathered by the various sources + * and processed by the {@link ResourceEventProcessingJob}. The + * implementation of this method should use the given information to + * update the index this manager is managing. + * </p> + * + * @param source + * The source that reported this resource event + * @param action + * The action to be taken on the given <code>resource</code> + * @param resource + * The index should perform the given <code>action</code> on + * this resource + * @param movePath + * If the given <code>action</code> is + * {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or + * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this + * field will not be null and reports the path the given + * <code>resource</code> was either moved from or moved to + * respectively. + * + * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN + * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX + * + * @see AbstractIndexManager#ACTION_ADD + * @see AbstractIndexManager#ACTION_REMOVE + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected abstract void performAction(byte source, byte action, IResource resource, IPath movePath); + + /** + * <p> + * Gets the working location of the manager. This is where any relevant + * state can be persisted. + * </p> + * + * @return the working location of the manager + */ + protected abstract IPath getWorkingLocation(); + + /** + * <p> + * Next time the manager starts up force a full workspace index + * </p> + */ + private void forceFullReIndexNextStart() { + IPath reIndexPath = AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME); + File file = new File(reIndexPath.toOSString()); + try { + file.createNewFile(); + } + catch (IOException e) { + Logger.logException(this.fName + ": Could not create file to tell manager to" + " do a full re-index on next load. " + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * @return <code>true</code> if a full workspace index is needed as + * dictated by a previous call to + * {@link #forceFullReIndexNextStart()}, <code>false</code> + * otherwise + */ + private boolean isForcedFullReIndexNeeded() { + boolean forcedFullReIndexNeeded = false; + IPath reIndexPath = AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME); + File file = new File(reIndexPath.toOSString()); + if (file.exists()) { + forcedFullReIndexNeeded = true; + file.delete(); + } + + return forcedFullReIndexNeeded; + } + + /** + * <p> + * A system {@link Job} used to visit all of the files in the workspace + * looking for files to index. + * </p> + * + * <p> + * This should only have to be done once per workspace on the first load, + * but if it fails or a SavedState can not be retrieved on a subsequent + * workspace load then this will have to be done again. + * </p> + */ + private class WorkspaceVisitorJob extends Job { + /** + * <p> + * Default constructor that sets up this job as a system job + * </p> + */ + protected WorkspaceVisitorJob() { + super(NLS.bind(SSECoreMessages.IndexManager_0_Processing_entire_workspace_for_the_first_time, AbstractIndexManager.this.fName)); + + this.setUser(false); + this.setSystem(true); + this.setPriority(Job.LONG); + } + + /** + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + try { + // update status + monitor.beginTask(NLS.bind(SSECoreMessages.IndexManager_0_Processing_entire_workspace_for_the_first_time, AbstractIndexManager.this.fName), IProgressMonitor.UNKNOWN); + + // visit the workspace + WorkspaceVisitor visitor = new WorkspaceVisitor(monitor); + ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.NONE); + + // process any remaining batched up resources to index + visitor.processBatchedResourceEvents(); + } + catch (CoreException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Failed visiting entire workspace for initial index. " + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); //$NON-NLS-1$ + } + + IStatus status; + if (monitor.isCanceled()) { + status = Status.CANCEL_STATUS; + } + else { + status = Status.OK_STATUS; + } + + monitor.done(); + return status; + } + + /** + * <p> + * An {@link IResourceProxyVisitor} used to visit all of the files in + * the workspace looking for files to add to the index. + * </p> + * + * <p> + * <b>NOTE: </b>After this visitor is used + * {@link WorkspaceVisitor#processBatchedResourceEvents() must be + * called to flush out the last of the {@link ResourceEvent}s produced + * by this visitor. + * </p> + */ + private class WorkspaceVisitor implements IResourceProxyVisitor { + /** + * {@link IProgressMonitor} used to report status and check for + * cancellation + */ + private SubMonitor fProgress; + + /** + * {@link Map}<{@link IResource}, {@link ResourceEvent}> + * <p> + * Map of resources events created and batched up by this visitor. + * These events are periodical be sent off to the + * {@link ResourceEventProcessingJob} but need to be sent off one + * final time after this visitor finishes it work. + * </p> + * + * @see #processBatchedResourceEvents() + */ + private Map fBatchedResourceEvents; + + /** + * <p> + * Default constructor + * </p> + * + * @param monitor + * used to report status and allow this visitor to be + * canceled + */ + protected WorkspaceVisitor(IProgressMonitor monitor) { + this.fProgress = SubMonitor.convert(monitor); + this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMOUNT); + } + + /** + * <p> + * As long as the monitor is not canceled visit each file in the + * workspace that should be visited. + * </p> + * + * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy) + * @see AbstractIndexManager#shouldVisit(String) + */ + public boolean visit(IResourceProxy proxy) throws CoreException { + this.fProgress.subTask(proxy.getName()); + + boolean visitChildren = false; + + /* + * if not canceled or a hidden resource then process file else + * don't visit children + */ + if (!this.fProgress.isCanceled()) { + if (proxy.isDerived()) { + /* + * Do not include derived resources + */ + visitChildren = false; + } + else if (proxy.requestFullPath().isRoot()) { + visitChildren = true; + } + else if (isResourceToIndex(proxy.getType(), proxy.requestFullPath())) { + if (proxy.getType() == IResource.FILE) { + // add the file to be indexed + IFile file = (IFile) proxy.requestResource(); + if (file.exists()) { + this.fBatchedResourceEvents.put(file, new ResourceEvent(AbstractIndexManager.SOURCE_WORKSPACE_SCAN, AbstractIndexManager.ACTION_ADD, null)); + } + } + visitChildren = true; + } + } + + // batch up resource changes before sending them out + if (this.fBatchedResourceEvents.size() >= BATCH_UP_AMOUNT) { + this.processBatchedResourceEvents(); + } + + return visitChildren; + } + + /** + * <p> + * Sends any batched up resource events created by this visitor to + * the {@link ResourceEventProcessingJob}. + * <p> + * + * <p> + * <b>NOTE:</b> This will be called every so often as the visitor + * is visiting resources but needs to be called a final time by + * the user of this visitor to be sure the final events are sent + * off + * </p> + */ + protected void processBatchedResourceEvents() { + AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(this.fBatchedResourceEvents); + this.fBatchedResourceEvents.clear(); + } + } + } + + /** + * <p> + * Used to listen to resource change events in the workspace. These events + * are batched up and then passed onto the + * {@link ResourceEventProcessingJob}. + * </p> + */ + private class ResourceChangeListener implements IResourceChangeListener { + /** + * <p> + * The number of events currently being processed by this listener. + * </p> + * <p> + * Use the {@link #fEventsBeingProcessedLock} when reading or writing + * this field + * </p> + * + * @see #fEventsBeingProcessedLock + */ + private volatile int fEventsBeingProcessed; + + /** + * Lock to use when reading or writing {@link #fEventsBeingProcessed} + * + * @see #fEventsBeingProcessed + */ + private final Object fEventsBeingProcessedLock = new Object(); + + /** + * <p> + * Current state of this listener + * </p> + * + * @see AbstractIndexManager#STATE_DISABLED + * @see AbstractIndexManager#STATE_ENABLED + */ + private volatile byte fState; + + /** + * <p> + * Default constructor + * </p> + */ + protected ResourceChangeListener() { + this.fState = STATE_DISABLED; + this.fEventsBeingProcessed = 0; + } + + /** + * <p> + * Start listening for resource change events + * </p> + */ + protected void start() { + this.fState = STATE_ENABLED; + ResourcesPlugin.getWorkspace().addResourceChangeListener(this); + } + + /** + * <p> + * Stop listening for resource change events and if already processing + * an event then wait for that processing to finish + * </p> + * + * @throws InterruptedException + * waiting for a current event to finish processing could + * be interrupted + */ + protected void stop() throws InterruptedException { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + + // wait indefinitely for current event to finish processing + this.waitForCurrentEvent(0); + + this.fState = STATE_DISABLED; + } + + /** + * <p> + * Blocks until either the current resource event has been processed + * or until the given timeout has passed. + * </p> + * + * @param timeout + * block until either this timeout elapses (0 means never + * to timeout) or the current resource change event + * finishes being processed + * + * @throws InterruptedException + * This can happen when waiting for a lock + */ + protected void waitForCurrentEvent(int timeout) throws InterruptedException { + synchronized (this.fEventsBeingProcessedLock) { + if (this.fEventsBeingProcessed != 0) { + this.fEventsBeingProcessedLock.wait(timeout); + } + } + } + + /** + * @return <code>true</code> if this listener is currently processing + * any events, <code>false</code> otherwise. + */ + protected boolean isProcessingEvents() { + return this.fEventsBeingProcessed != 0; + } + + /** + * <p> + * Process a resource change event. If it is a pre-close or pre-delete + * then the {@link ResourceEventProcessingJob} is paused so it does + * not try to process resources that are about to be deleted. The + * {@link ResourceDeltaVisitor} is used to actually process the event. + * </p> + * + * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) + * @see ResourceDeltaVisitor + */ + public void resourceChanged(IResourceChangeEvent event) { + try { + // update the number of events being processed + synchronized (this.fEventsBeingProcessedLock) { + ++this.fEventsBeingProcessed; + } + + if (this.fState == STATE_ENABLED) { + switch (event.getType()) { + case IResourceChangeEvent.PRE_CLOSE : + case IResourceChangeEvent.PRE_DELETE : { + /* + * pause the persister job so it does not + * interfere + */ + AbstractIndexManager.this.fResourceEventProcessingJob.pause(); + break; + } + case IResourceChangeEvent.POST_BUILD : + case IResourceChangeEvent.POST_CHANGE : { + // post change start up the indexer job and + // process the delta + AbstractIndexManager.this.fResourceEventProcessingJob.unPause(); + + // only analyze the full (starting at root) delta + // hierarchy + IResourceDelta delta = event.getDelta(); + if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$ + try { + // use visitor to visit all children + ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(AbstractIndexManager.SOURCE_RESOURCE_CHANGE); + delta.accept(visitor, false); + + // process any remaining batched up + // resources to index + visitor.processBatchedResourceEvents(); + } + catch (CoreException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Failed visiting resource change delta. " + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); //$NON-NLS-1$ + } + } + break; + } + } + } + else { + Logger.log(Logger.ERROR, "A resource change event came in after " + //$NON-NLS-1$ + AbstractIndexManager.this.fName + " shut down. This should never " + //$NON-NLS-1$ + "ever happen, but if it does the index may now be inconsistant."); //$NON-NLS-1$ + } + } + finally { + // no matter how we exit be sure to update the number of + // events being processed + synchronized (this.fEventsBeingProcessedLock) { + --this.fEventsBeingProcessed; + + // if currently not events being processed, then notify + if (this.fEventsBeingProcessed == 0) { + this.fEventsBeingProcessedLock.notifyAll(); + } + } + } + } + } + + /** + * <p> + * Used to visit {@link IResourceDelta}s from both the + * {@link IResourceChangeListener} and from a {@link ISaveParticipant} + * given to + * {@link AbstractIndexManager#start(IResourceDelta, IProgressMonitor)}. + * The resource events are batched into groups of + * {@link AbstractIndexManager#BATCH_UP_AMOUNT} before being passed onto + * the {@link ResourceEventProcessingJob}. + * </p> + * + * <p> + * <b>NOTE 1: </b> This class is intended for one time use, thus a new + * instance should be instantiated each time this visitor is needed to + * process a new {@link IResourceDelta}. + * </p> + * + * <p> + * <b>NOTE 2: </b> Be sure to call + * {@link ResourceDeltaVisitor#processBatchedResourceEvents()} after using + * this visitor to be sure any remaining events get passed onto the + * {@link ResourceEventProcessingJob}. + * </p> + * + * @see ResourceDeltaVisitor#processBatchedResourceEvents() + */ + private class ResourceDeltaVisitor implements IResourceDeltaVisitor { + /** {@link IProgressMonitor} used to report status */ + private SubMonitor fProgress; + + /** + * <p> + * The source that should be used when sending resource events to the + * {@link ResourceEventProcessingJob}. + * </p> + * + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE + */ + private byte fSource; + + /** + * <p> + * Due to the nature of a visitor it has no way of knowing the total + * amount of work it has to do but it can start to predict it based on + * the number of children of each event it processes and whether it + * plans on visiting those children + * </p> + */ + private int fPredictedWorkRemaining; + + /** + * {@link Map}<{@link IResource}, {@link ResourceEvent}> + * <p> + * Map of resources events created and batched up by this visitor. + * These events are periodical be sent off to the + * {@link ResourceEventProcessingJob} but need to be sent off one + * final time after this visitor finishes it work. + * </p> + * + * @see #processBatchedResourceEvents() + */ + private Map fBatchedResourceEvents; + + /** + * <p> + * Creates a visitor that will create resource events based on the + * resources it visits and using the given source as the source of the + * events. + * </p> + * + * @param source + * The source of the events that should be used when + * creating resource events from visited resources + * + * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + */ + protected ResourceDeltaVisitor(byte source) { + this(SubMonitor.convert(null), source); + } + + /** + * <p> + * Creates a visitor that will create resource events based on the + * resources it visits and using the given source as the source of the + * events and report its status to the given progress as best it can + * as it visits resources. + * </p> + * + * <p> + * <b>NOTE:</b> While the {@link SubMonitor} is provided to report + * status the visitor will not honor any cancellation requests. + * </p> + * + * @param progress + * Used to report status. This visitor can <b>not</b> be + * canceled + * @param source + * The source of the events that should be used when + * creating resource events from visited resources + * + * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + */ + protected ResourceDeltaVisitor(SubMonitor progress, byte source) { + this.fProgress = progress; + this.fSource = source; + this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMOUNT); + this.fPredictedWorkRemaining = 1; + } + + /** + * <p> + * Transforms each {@link IResourceDelta} into a {@link ResourceEvent} + * . Batches up these {@link ResourceEvent}s and then passes them onto + * the {@link ResourceEventProcessingJob}. + * </p> + * + * <p> + * <b>NOTE 1: </b> Be sure to call + * {@link ResourceDeltaVisitor#processBatchedResourceEvents()} after + * using this visitor to be sure any remaining events get passed onto + * the {@link ResourceEventProcessingJob}. + * </p> + * + * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) + * @see #processBatchedResourceEvents() + */ + public boolean visit(IResourceDelta delta) throws CoreException { + // report status + this.fProgress.subTask(NLS.bind(SSECoreMessages.IndexManager_0_resources_to_go_1, new Object[]{"" + fPredictedWorkRemaining, delta.getFullPath().toString()})); //$NON-NLS-1$ + + // process delta if resource not hidden + boolean visitChildren = false; + + /* + * if root node always visit its children else ask manager + * implementation if resource and its children should be visited + */ + if (delta.getResource().isDerived()) { // Do not include derived + // resources + visitChildren = false; + } + else if (delta.getFullPath().isRoot()) { //$NON-NLS-1$ + visitChildren = true; + } + else { + IResource resource = delta.getResource(); + + // check if should index resource or its children + if (isResourceToIndex(resource.getType(), resource.getFullPath())) { + if (resource.getType() == IResource.FILE) { + + switch (delta.getKind()) { + case IResourceDelta.CHANGED : { + /* + * ignore any change that is not a CONTENT, + * REPLACED, TYPE, or MOVE_FROM change + */ + if (!((delta.getFlags() & IResourceDelta.CONTENT) != 0) && !((delta.getFlags() & IResourceDelta.REPLACED) != 0) && !((delta.getFlags() & IResourceDelta.TYPE) != 0) && !(((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0))) { + + break; + } + } + /* + * it is intended that sometimes a change will + * fall through to add + */ + //$FALL-THROUGH$ + case IResourceDelta.ADDED : { + if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) { + // create add move from action + this.fBatchedResourceEvents.put(resource, new ResourceEvent(this.fSource, AbstractIndexManager.ACTION_ADD_MOVE_FROM, delta.getMovedFromPath())); + + } + else { + // create add action + this.fBatchedResourceEvents.put(resource, new ResourceEvent(this.fSource, AbstractIndexManager.ACTION_ADD, null)); + } + + break; + } + case IResourceDelta.REMOVED : { + if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) { + // create remove move to action + this.fBatchedResourceEvents.put(resource, new ResourceEvent(this.fSource, AbstractIndexManager.ACTION_REMOVE_MOVE_TO, delta.getMovedToPath())); + } + else { + // create remove action + this.fBatchedResourceEvents.put(resource, new ResourceEvent(this.fSource, AbstractIndexManager.ACTION_REMOVE, null)); + } + break; + } + } + }// end is file + + visitChildren = true; + } + else { + visitChildren = false; + } + + + + // deal with trying to report progress + if (visitChildren) { + this.fPredictedWorkRemaining += delta.getAffectedChildren().length; + } + this.fProgress.setWorkRemaining(this.fPredictedWorkRemaining); + this.fProgress.worked(1); + --this.fPredictedWorkRemaining; + + // batch up resource changes before sending them out + if (this.fBatchedResourceEvents.size() >= BATCH_UP_AMOUNT) { + this.processBatchedResourceEvents(); + } + } + + return visitChildren; + } + + /** + * <p> + * Sends any batched up resource events created by this visitor to the + * {@link ResourceEventProcessingJob}. + * <p> + * + * <p> + * <b>NOTE:</b> This will be called every so often as the visitor is + * visiting resources but needs to be called a final time by the user + * of this visitor to be sure the final events are sent off + * </p> + */ + protected void processBatchedResourceEvents() { + AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(this.fBatchedResourceEvents); + this.fBatchedResourceEvents.clear(); + } + } + + /** + * <p> + * Collects {@link ResourceEvent}s from the different sources and then + * processes each one by calling + * {@link AbstractIndexManager#performAction(byte, byte, IResource, IPath)} + * for each {@link ResourceEvent}. + * </p> + * + * @see AbstractIndexManager#performAction(byte, byte, IResource, IPath) + */ + private class ResourceEventProcessingJob extends Job { + /** Length to delay when scheduling job */ + private static final int DELAY = 500; + + /** + * <p> + * Name of the file where resource events still to index will be + * preserved for the next start up. + * </p> + */ + private static final String PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME = ".preservedResourceEvents"; //$NON-NLS-1$ + + /** + * <p> + * This needs to be updated if + * {@link #preserveReceivedResourceEvents()} ever changes how it + * persists resource events so that + * {@link #loadPreservedReceivedResourceEvents(SubMonitor)} knows when + * opening a file that the format is of the current version and if not + * knows it does not know how to read the older version. + * </p> + * + * @see #preserveReceivedResourceEvents() + * @see #loadPreservedReceivedResourceEvents(SubMonitor) + */ + private static final long serialVersionUID = 1L; + + /** Whether this job has been paused or not */ + private volatile boolean fIsPaused; + + /** + * {@link Map}<{@link IResource}, {@link ResourceEvent}> + * <p> + * The list of resources events to be processed + * </p> + */ + private Map fResourceEvents; + + /** Lock used when accessing {@link #fBatchedResourceEvents} */ + private final Object fResourceEventsLock = new Object(); + + /** + * Locked used for allowing other jobs to wait on this job. This job + * will notify those waiting on this lock whenever it is done + * processing all resource events it currently knows about. + * + * @see #waitForConsistant(int) + */ + private final Object fToNotifyLock = new Object(); + + /** + * <p> + * Sets up this job as a long running system job + * </p> + */ + protected ResourceEventProcessingJob() { + super(NLS.bind(SSECoreMessages.IndexManager_0_Processing_resource_events, AbstractIndexManager.this.fName)); + + // set this up as a long running system job + this.setUser(false); + this.setSystem(true); + this.setPriority(Job.LONG); + + this.fIsPaused = false; + this.fResourceEvents = new LinkedHashMap(); + } + + /** + * <p> + * Loads any preserved {@link ResourceEvent}s from the last time + * {@link #stop(boolean)} was invoked and schedules the job to be run + * </p> + * + * <p> + * <b>NOTE: </b>Should be used instead of of calling + * {@link Job#schedule()} because this method also takes care of + * loading preserved state. + * </p> + * + * @param loadPreservedResourceEvents + * <code>true</code> if should load any preserved + * {@link ResourceEvent}s from the last time + * {@link #stop(boolean)} was invoked + * + * @return <code>true</code> if either + * <code>loadPreservedResourceEvents</code> was false or there + * was success in loading the preserved {@link ResourceEvent} + * s. If <code>false</code> then some {@link ResourceEvent}s + * may have been loosed and to insure index consistency with + * the workspace a full workspace re-index is needed. + * + * @see #stop(boolean) + */ + protected synchronized boolean start(boolean loadPreservedResourceEvents, SubMonitor progress) { + + boolean successLoadingPreserved = true; + + // attempt to load preserved resource events if requested + if (!loadPreservedResourceEvents) { + File preservedResourceEventsFile = this.getPreservedResourceEventsFile(); + preservedResourceEventsFile.delete(); + } + else { + successLoadingPreserved = this.loadPreservedReceivedResourceEvents(progress); + } + + // start up the job + this.schedule(); + + return successLoadingPreserved; + } + + /** + * <p> + * Immediately stops the job and preserves any {@link ResourceEvent}s + * in the queue to be processed by not yet processed if requested + * </p> + * + * @param preserveResourceEvents + * <code>true</code> to preserve any {@link ResourceEvent}s + * in the queue yet to be processed, <code>false</code> + * otherwise + * + * @return <code>true</code> if either + * <code>preserveResourceEvents</code> is <code>false</code> + * or if there was success in preserving the + * {@link ResourceEvent}s yet to be processed. If + * <code>false</code> then the preserving failed and a full + * workspace re-processing is needed the next time the manager + * is started + * + * @throws InterruptedException + * This could happen when trying to cancel or join the job + * in progress, but it really shouldn't + * + * @see #start(boolean, SubMonitor) + */ + protected synchronized boolean stop(boolean preserveResourceEvents) throws InterruptedException { + // this will not block indefinitely because it is known this job + // can be canceled + this.cancel(); + this.join(); + + // preserve if requested, else be sure no preserve file is left + // over for next start + boolean success = true; + if (preserveResourceEvents && this.hasResourceEventsToProcess()) { + success = this.preserveReceivedResourceEvents(); + } + else { + this.getPreservedResourceEventsFile().delete(); + } + + return success; + } + + /** + * @return <code>true</code> if job is currently running or paused + * + * @see #pause() + * @see #unPause() + */ + protected synchronized boolean isProcessing() { + return this.getState() != Job.NONE || this.fIsPaused; + } + + /** + * <p> + * Un-pauses this job. This has no effect if the job is already + * running. + * </p> + * <p> + * This should be used in place of {@link Job#schedule()} to reset + * state caused by calling {@link #pause()} + * </p> + * + * @see #pause() + */ + protected synchronized void unPause() { + this.fIsPaused = false; + + // get the job running again depending on its current state + if (this.getState() == Job.SLEEPING) { + this.wakeUp(DELAY); + } + else { + this.schedule(DELAY); + } + } + + /** + * <p> + * Pauses this job, even if it is running + * </p> + * <p> + * This should be used in place of {@link Job#sleep()} because + * {@link Job#sleep()} will not pause a job that is already running + * but calling this will pause this job even if it is running. + * {@link #unPause()} must be used to start this job again + * </p> + * + * @see #unPause() + */ + protected synchronized void pause() { + // if job is already running this will force it to pause + this.fIsPaused = true; + + // this only works if the job is not running + this.sleep(); + } + + /** + * <p> + * Adds a batch of {@link ResourceEvent}s to the queue of events to be + * processed. Will also un-pause the job if it is not already running + * </p> + * + * @param resourceEvents + * {@link Map}<{@link IResource}, {@link ResourceEvent} + * > A batch of {@link ResourceEvent}s to be processed + * + * @see #addResourceEvent(ResourceEvent) + * @see #unPause() + */ + protected void addResourceEvents(Map resourceEvents) { + Iterator iter = resourceEvents.keySet().iterator(); + while (iter.hasNext()) { + IResource resource = (IResource) iter.next(); + ResourceEvent resourceEvent = (ResourceEvent) resourceEvents.get(resource); + addResourceEvent(resource, resourceEvent); + } + + // un-pause the processor if it is not already running + if (!isProcessing()) { + this.unPause(); + } + } + + /** + * <p> + * Gets the number of {@link ResourceEvent}s left to process by this + * job. This count is only valid for the exact moment it is returned + * because events are constantly being added and removed from the + * queue of events to process + * </p> + * + * @return the number of {@link ResourceEvent}s left to process + */ + protected int getNumResourceEventsToProcess() { + return this.fResourceEvents.size(); + } + + /** + * <p> + * Blocks until either the given timeout elapses (0 means never to + * timeout), or there are currently no {@link ResourceEvent}s to + * process or being processed by this job + * </p> + * + * @param timeout + * block until either this timeout elapses (0 means never + * to timeout) or there are currently no + * {@link ResourceEvent}s to process or being processed by + * this job + * + * @throws InterruptedException + * This can happen when waiting for a lock + */ + protected void waitForConsistant(int timeout) throws InterruptedException { + if (hasResourceEventsToProcess() || isProcessing()) { + synchronized (this.fToNotifyLock) { + this.fToNotifyLock.wait(timeout); + } + } + } + + /** + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + try { + // report status + SubMonitor progress = SubMonitor.convert(monitor); + + while (!this.fIsPaused && !monitor.isCanceled() && this.hasResourceEventsToProcess()) { + // report status + progress.setTaskName(NLS.bind(SSECoreMessages.IndexManager_0_Indexing_1_Files, new Object[]{AbstractIndexManager.this.fName, "" + getNumResourceEventsToProcess()})); //$NON-NLS-1$ + progress.setWorkRemaining(getNumResourceEventsToProcess()); + + // get the next event to process + ResourceEvent resourceEvent = null; + IResource resource = null; + synchronized (this.fResourceEventsLock) { + resource = (IResource) this.fResourceEvents.keySet().iterator().next(); + resourceEvent = (ResourceEvent) this.fResourceEvents.remove(resource); + } + + // report status + monitor.subTask(resource.getName()); + + // perform action safely + final byte source = resourceEvent.fSource; + final byte action = resourceEvent.fAction; + final IResource finResource = resource; + final IPath movePath = resourceEvent.fMovePath; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + AbstractIndexManager.this.performAction(source, action, finResource, movePath); + } + + public void handleException(Throwable e) { + Logger.logException("Error while performing an update to the index. " + //$NON-NLS-1$ + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); + } + }); + + // report progress + progress.worked(1); + + // avoid dead locks + Job.getJobManager().currentJob().yieldRule(monitor); + } + + // done work + monitor.done(); + } + finally { + // want to be sure we notify no matter how we exit + this.notifyIfConsistant(); + } + + /* + * if canceled then return CANCEL, else if done or paused return + * OK + */ + IStatus exitStatus; + if (monitor.isCanceled()) { + exitStatus = Status.CANCEL_STATUS; + } + else { + exitStatus = Status.OK_STATUS; + } + + return exitStatus; + } + + /** + * <p> + * If resource not already scheduled to be processed, schedule it else + * if resource already scheduled to be processed, update the action + * only if the new action comes from a resource change event. + * </p> + * + * <p> + * Ignore other sources for updating existing resource events because + * all other sources are "start-up" type sources and thus only + * {@link ResourceEvent} with a source of a resource change event + * trump existing events. + * </p> + * + * @param resourceEvent + * {@link ResourceEvent} to be processed by this job + */ + private void addResourceEvent(IResource resource, ResourceEvent resourceEvent) { + + synchronized (this.fResourceEventsLock) { + /* + * if resource not already scheduled to be processed, schedule + * it else if resource already scheduled to be processed, + * update the action only if the new action comes from a + * resource change event + */ + if (!this.fResourceEvents.containsKey(resource)) { + this.fResourceEvents.put(resource, resourceEvent); + } + else if (resourceEvent.fSource == AbstractIndexManager.SOURCE_RESOURCE_CHANGE) { + ((ResourceEvent) this.fResourceEvents.get(resource)).fAction = resourceEvent.fAction; + } + else { + // Purposely ignoring all other resource events + } + } + } + + /** + * @return <code>true</code> if there are any resources to process, + * <code>false</code> otherwise + */ + private boolean hasResourceEventsToProcess() { + return !this.fResourceEvents.isEmpty(); + } + + /** + * <p> + * Preserves all of the resource events that have been received by + * this manager but not yet processed + * </p> + * + * <p> + * If this operation was successful then the next time the manager + * starts it can load these events and process them. If it was not + * successful then a full re-processing of the entire workspace will + * need to take place to be sure the index is consistent. + * </p> + * + * <p> + * <b>NOTE:</b> If this method changes how it preserves these events + * then {@link #serialVersionUID} will need to be incremented so that + * the manager does not attempt to load an old version of the file + * that may exist in a users workspace. Also + * {@link #loadPreservedReceivedResourceEvents(SubMonitor)} will have + * to be updated to load the new file structure. + * </p> + * + * @return <code>true</code> if successfully preserved the resource + * events that have been received by not yet processed, + * <code>false</code> otherwise + * + * @see #serialVersionUID + * @see #loadPreservedReceivedResourceEvents(SubMonitor) + */ + private boolean preserveReceivedResourceEvents() { + File preservedResourceEventsFile = this.getPreservedResourceEventsFile(); + boolean success = true; + + synchronized (this.fResourceEventsLock) { + DataOutputStream dos = null; + try { + // if file already exists delete it + if (preservedResourceEventsFile.exists()) { + preservedResourceEventsFile.delete(); + preservedResourceEventsFile.createNewFile(); + } + + // create output objects + FileOutputStream fos = new FileOutputStream(preservedResourceEventsFile); + BufferedOutputStream bos = new BufferedOutputStream(fos); + dos = new DataOutputStream(bos); + + // write serial version + dos.writeLong(serialVersionUID); + + // write size + dos.writeInt(this.getNumResourceEventsToProcess()); + + // write out all the information needed to restore the + // resource events to process + Iterator iter = this.fResourceEvents.keySet().iterator(); + while (iter.hasNext()) { + IResource resource = (IResource) iter.next(); + ResourceEvent resourceEvent = (ResourceEvent) this.fResourceEvents.get(resource); + + if (resourceEvent.fSource != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) { + // write out information + dos.writeByte(resourceEvent.fAction); + dos.writeByte(resource.getType()); + byte[] pathBytes = resource.getFullPath().toString().getBytes(ENCODING_UTF16); + dos.write(pathBytes); + dos.writeByte('\0'); + if (resourceEvent.fMovePath != null) { + dos.writeBytes(resourceEvent.fMovePath.toPortableString()); + } + dos.writeByte('\0'); + } + } + + this.fResourceEvents.clear(); + + dos.flush(); + } + catch (FileNotFoundException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Exception while opening file to preserve resources to index.", //$NON-NLS-1$ + e); + success = false; + } + catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Exception while writing to file to preserve resources to index.", //$NON-NLS-1$ + e); + success = false; + } + finally { + // be sure to close output + if (dos != null) { + try { + dos.close(); + } + catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Exception while closing file with preserved resources to index.", //$NON-NLS-1$ + e); + success = false; + } + } + } + + // if failed, for consistency must do a full re-process next + // workspace load + if (!success) { + preservedResourceEventsFile.delete(); + } + } + + return success; + } + + /** + * <p> + * Loads the received resource events that were preserved during the + * manager's last shut down so they can be processed now + * </p> + * + * <p> + * If this operation is not successful then a full re-processing of + * the entire workspace is needed to be sure the index is consistent. + * </p> + * + * @param progress + * used to report status of loading the preserved received + * resource events + * @return <code>true</code> if the loading of the preserved received + * resource events was successful, <code>false</code> + * otherwise. + * + * @see #serialVersionUID + * @see #preserveReceivedResourceEvents() + */ + private boolean loadPreservedReceivedResourceEvents(SubMonitor progress) { + progress.subTask(SSECoreMessages.IndexManager_processing_deferred_resource_changes); + + boolean success = true; + File preservedResourceEventsFile = this.getPreservedResourceEventsFile(); + + if (preservedResourceEventsFile.exists()) { + Map preservedResourceEvents = null; + + DataInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(preservedResourceEventsFile); + BufferedInputStream bis = new BufferedInputStream(fis); + dis = new DataInputStream(bis); + + // check serial version first + long preservedSerialVersionUID = dis.readLong(); + if (preservedSerialVersionUID == serialVersionUID) { + + // read each record + int numberOfRecords = dis.readInt(); + preservedResourceEvents = new LinkedHashMap(numberOfRecords); + progress.setWorkRemaining(numberOfRecords); + for (int i = 0; i < numberOfRecords; ++i) { + // action is first byte + byte action = dis.readByte(); + + // file type is the next byte + byte fileType = dis.readByte(); + + // resource location are the next bytes + ByteArrayOutputStream resourceLocationStream = new ByteArrayOutputStream(); + byte b = dis.readByte(); + while (b != '\0') { + resourceLocationStream.write(b); + b = dis.readByte(); + } + + // move path are the next bytes + ByteArrayOutputStream movePathBOS = new ByteArrayOutputStream(); + b = dis.readByte(); + while (b != '\0') { + movePathBOS.write(b); + b = dis.readByte(); + } + + // get the resource + IResource resource = null; + IPath resourcePath = new Path(new String(resourceLocationStream.toByteArray(), ENCODING_UTF16)); + if (!resourcePath.isRoot() && resourcePath.segmentCount() > 1) { + if (fileType == IResource.FILE) { + resource = ResourcesPlugin.getWorkspace().getRoot().getFile(resourcePath); + } + else { + resource = ResourcesPlugin.getWorkspace().getRoot().getFolder(resourcePath); + } + } + else { + Logger.log(Logger.WARNING, "The AbstractIndexManager " + AbstractIndexManager.this.fName + " attempted to load an invlaid preserved resource event:\n" + "(" + resourcePath + ")"); + } + + // get the move path + IPath movePath = null; + if (movePathBOS.size() != 0) { + movePath = new Path(new String(movePathBOS.toByteArray(), ENCODING_UTF16)); + } + + // add the object to the list of of preserved + // resources + preservedResourceEvents.put(resource, new ResourceEvent(AbstractIndexManager.SOURCE_PRESERVED_RESOURCES_TO_INDEX, action, movePath)); + + progress.worked(1); + } + } + else { + success = false; + } + } + catch (FileNotFoundException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Exception while opening file to read preserved resources to index. Index manager will recover by re-indexing workspace.", //$NON-NLS-1$ + e); + success = false; + } + catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Exception while reading from file of preserved resources to index. Index manager will recover by re-indexing workspace.", //$NON-NLS-1$ + e); + success = false; + } + catch (Exception e) { + // Purposely catching all exceptions here so that index + // manager can recover gracefully + Logger.logException(AbstractIndexManager.this.fName + ": Unexpected exception while reading from file of preserved resources to index. Index manager will recover by re-indexing workspace.", //$NON-NLS-1$ + e); + success = false; + } + finally { + if (dis != null) { + try { + dis.close(); + } + catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + ": Exception while closing file of preserved resources" + //$NON-NLS-1$ + " to index that was just read. This should have no" + //$NON-NLS-1$ + " effect on the consistency of the index.", //$NON-NLS-1$ + e); + } + } + } + + // if success loading preserved then add to master list + if (success && preservedResourceEvents != null) { + synchronized (this.fResourceEventsLock) { + Iterator iter = preservedResourceEvents.keySet().iterator(); + while (iter.hasNext()) { + IResource resource = (IResource) iter.next(); + ResourceEvent event = (ResourceEvent) preservedResourceEvents.get(resource); + this.fResourceEvents.put(resource, event); + } + } + } + else { + // failed reading file, so delete it + preservedResourceEventsFile.delete(); + } + } + + progress.done(); + return success; + } + + /** + * @return {@link File} that contains any resource events received but + * not processed by this manager the last time it shutdown. + * This file may or may not actually exist. + * + * @see #preserveReceivedResourceEvents() + * @see #loadPreservedReceivedResourceEvents(SubMonitor) + */ + private File getPreservedResourceEventsFile() { + IPath preservedResourcesToIndexPath = AbstractIndexManager.this.getWorkingLocation().append(PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME); + return new File(preservedResourcesToIndexPath.toOSString()); + } + + /** + * <p> + * If all resource events have been processed + */ + private void notifyIfConsistant() { + if (!this.hasResourceEventsToProcess()) { + synchronized (this.fToNotifyLock) { + this.fToNotifyLock.notifyAll(); + } + } + } + } + + /** + * <p> + * Represents a resource that was discovered by this manager. Contains all + * the information this manager and the index needs to know about this + * resource. Such has how the manager was notified about this resource and + * the type of action occurring on the resource. + * </p> + */ + private static class ResourceEvent { + /** + * <p> + * The source of this resource event + * </p> + * + * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN + * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX + */ + protected byte fSource; + + /** + * <p> + * The action that the index should take with this resource + * </p> + * + * @see AbstractIndexManager#ACTION_ADD + * @see AbstractIndexManager#ACTION_REMOVE + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected byte fAction; + + /** + * + * <p> + * If the {@link #fAction} is + * {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or + * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field + * will have the path the resource was moved from or moved to. Else + * this field will be <code>null</code> + * </p> + * + * <p> + * <b>NOTE: </b>Maybe <code>null</code>. + * </p> + * + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected IPath fMovePath; + + /** + * <p> + * Creates a resource event that the index needs to react to in some + * way + * </p> + * + * @param source + * source that the manager used to learn of this resource + * @param action + * action the index should take on this resource + * @param resource + * resource that the index should know about + * @param movePath + * if action is + * {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or + * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then + * this should be the path the resource was moved from or + * moved to respectively, else should be <code>null</code> + * + * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN + * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX + * + * @see AbstractIndexManager#ACTION_ADD + * @see AbstractIndexManager#ACTION_REMOVE + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected ResourceEvent(byte source, byte action, IPath movePath) { + this.fSource = source; + this.fAction = action; + this.fMovePath = movePath; + } + } +} \ No newline at end of file
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/FileBufferModelManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/FileBufferModelManager.java new file mode 100644 index 0000000..6ae82d5 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/FileBufferModelManager.java
@@ -0,0 +1,838 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300434 - Make inner classes static where possible + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.IFileBuffer; +import org.eclipse.core.filebuffers.IFileBufferListener; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filebuffers.ITextFileBufferManager; +import org.eclipse.core.filebuffers.LocationKind; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin; +import org.eclipse.wst.common.uriresolver.internal.util.URIHelper; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.model.AbstractStructuredModel; +import org.eclipse.wst.sse.core.internal.modelhandler.ModelHandlerRegistry; +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.util.URIResolver; + +/** + * Not intended to be subclassed, referenced or instantiated by clients. + * + * This class is responsible for coordinating the creation and disposal of + * structured models built on structured documents found in FileBuffers. It + * allows the SSE Model Manager to act as a client to the + * TextFileBufferManager. + */ +public class FileBufferModelManager { + + static class DocumentInfo { + /** + * The ITextFileBuffer + */ + ITextFileBuffer buffer = null; + + /** + * The platform content-type ID of this document + */ + String contentTypeID = null; + + /** + * The IStructureModel containing this document; might be null at + * points in the ITextFileBuffer's lifecycle + */ + IStructuredModel model = null; + + /** + * Whether FileBufferModelManager called connect() for this + * DocumentInfo's text filebuffer + */ + boolean selfConnected = false; + + int bufferReferenceCount = 0; + int modelReferenceCount = 0; + + /** + * The default value is the "compatibility" kind from before there was + * a LocationKind hint object--this is expected to be overridden at + * runtime. + */ + LocationKind locationKind = LocationKind.NORMALIZE; + } + + /** + * A URIResolver instance of models built on java.io.Files + */ + static class ExternalURIResolver implements URIResolver { + IPath fLocation; + + ExternalURIResolver(IPath location) { + fLocation = location; + } + + public String getFileBaseLocation() { + if (fLocation == null) + return null; + else + return fLocation.toString(); + } + + public String getLocationByURI(String uri) { + return getLocationByURI(uri, getFileBaseLocation(), false); + } + + public String getLocationByURI(String uri, boolean resolveCrossProjectLinks) { + return getLocationByURI(uri, getFileBaseLocation(), resolveCrossProjectLinks); + } + + public String getLocationByURI(String uri, String baseReference) { + return getLocationByURI(uri, baseReference, false); + } + + public String getLocationByURI(String uri, String baseReference, boolean resolveCrossProjectLinks) { + // ignore resolveCrossProjectLinks value + if (uri == null) + return null; + if (uri.startsWith("file:")) { //$NON-NLS-1$ + try { + URL url = new URL(uri); + return url.getFile(); + } + catch (MalformedURLException e) { + } + } + return URIHelper.normalize(uri, baseReference, Path.ROOT.toString()); + } + + public IProject getProject() { + return null; + } + + public IContainer getRootLocation() { + return ResourcesPlugin.getWorkspace().getRoot(); + } + + public InputStream getURIStream(String uri) { + return null; + } + + public void setFileBaseLocation(String newLocation) { + if (newLocation != null) + fLocation = new Path(newLocation); + else + fLocation = null; + } + + public void setProject(IProject newProject) { + } + } + + /** + * A URIResolver instance of models built on the extensible WST URI + * resolver + */ + static class CommonURIResolver implements URIResolver { + String fLocation; + IPath fPath; + private IProject fProject; + final static String SEPARATOR = "/"; //$NON-NLS-1$ + final static String FILE_PREFIX = "file://"; //$NON-NLS-1$ + + CommonURIResolver(IFile workspaceFile) { + fPath = workspaceFile.getFullPath(); + fProject = workspaceFile.getProject(); + } + + public String getFileBaseLocation() { + return fLocation; + } + + public String getLocationByURI(String uri) { + return getLocationByURI(uri, getFileBaseLocation(), false); + } + + public String getLocationByURI(String uri, boolean resolveCrossProjectLinks) { + return getLocationByURI(uri, getFileBaseLocation(), resolveCrossProjectLinks); + } + + public String getLocationByURI(String uri, String baseReference) { + return getLocationByURI(uri, baseReference, false); + } + + public String getLocationByURI(String uri, String baseReference, boolean resolveCrossProjectLinks) { + boolean baseHasPrefix = baseReference != null && baseReference.startsWith(FILE_PREFIX); + String reference = null; + if (baseHasPrefix) { + reference = baseReference; + } + else { + reference = FILE_PREFIX + baseReference; + } + String result = URIResolverPlugin.createResolver().resolve(reference, null, uri); + // Logger.log(Logger.INFO_DEBUG, + // "URIResolverPlugin.createResolver().resolve(" + // + reference + ", null, " +uri+") = " + result); + if (!baseHasPrefix && result.startsWith(FILE_PREFIX) && result.length() > FILE_PREFIX.length()) { + result = result.substring(FILE_PREFIX.length()); + } + return result; + } + + public IProject getProject() { + return fProject; + } + + public IContainer getRootLocation() { + String root = URIResolverPlugin.createResolver().resolve(FILE_PREFIX + getFileBaseLocation(), null, SEPARATOR); + IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(new Path(root)); + for (int i = 0; i < files.length; i++) { + if ((files[i].getType() & IResource.FOLDER) == IResource.FOLDER) { + if (fPath.isPrefixOf(((IFolder) files[i]).getFullPath())) { + return (IFolder) files[i]; + } + } + } + return getProject(); + } + + public InputStream getURIStream(String uri) { + return null; + } + + public void setFileBaseLocation(String newLocation) { + fLocation = newLocation; + } + + public void setProject(IProject newProject) { + fProject = newProject; + } + } + + /** + * Maps interesting documents in file buffers to those file buffers. + * Required to allow us to go from the document instances to complete + * models. + */ + class FileBufferMapper implements IFileBufferListener { + public void bufferContentAboutToBeReplaced(IFileBuffer buffer) { + } + + public void bufferContentReplaced(IFileBuffer buffer) { + } + + public void bufferCreated(IFileBuffer buffer) { + if (buffer instanceof ITextFileBuffer) { + ITextFileBuffer textBuffer = (ITextFileBuffer) buffer; + if (!(textBuffer.getDocument() instanceof IStructuredDocument)) + return; + + if (Logger.DEBUG_TEXTBUFFERLIFECYCLE) { + Logger.log(Logger.INFO, "Learned new buffer: " + buffer.getLocation().toString() + " " + buffer + " " + ((ITextFileBuffer) buffer).getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + DocumentInfo info = new DocumentInfo(); + info.buffer = textBuffer; + info.contentTypeID = detectContentType(buffer.getLocation()).getId(); + info.bufferReferenceCount++; + fDocumentMap.put(textBuffer.getDocument(), info); + } + } + + public void bufferDisposed(IFileBuffer buffer) { + if (buffer instanceof ITextFileBuffer) { + ITextFileBuffer textBuffer = (ITextFileBuffer) buffer; + if (!(textBuffer.getDocument() instanceof IStructuredDocument)) + return; + if (Logger.DEBUG_TEXTBUFFERLIFECYCLE) { + Logger.log(Logger.INFO, "Discarded buffer: " + buffer.getLocation().toString() + " " + buffer + " " + ((ITextFileBuffer) buffer).getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + DocumentInfo info = (DocumentInfo) fDocumentMap.get(textBuffer.getDocument()); + if (info != null) { + info.bufferReferenceCount--; + checkReferenceCounts(info, textBuffer.getDocument()); + } + } + } + + public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) { + if (buffer instanceof ITextFileBuffer) { + if (Logger.DEBUG_TEXTBUFFERLIFECYCLE) { + Logger.log(Logger.INFO, "Buffer dirty state changed: (" + isDirty + ") " + buffer.getLocation().toString() + " " + buffer + " " + ((ITextFileBuffer) buffer).getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + ITextFileBuffer textBuffer = (ITextFileBuffer) buffer; + if (!(textBuffer.getDocument() instanceof IStructuredDocument)) + return; + DocumentInfo info = (DocumentInfo) fDocumentMap.get(textBuffer.getDocument()); + if (info != null && info.model != null) { + String msg = "Updating model dirty state for" + info.buffer.getLocation(); //$NON-NLS-1$ + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT || Logger.DEBUG_TEXTBUFFERLIFECYCLE) { + Logger.log(Logger.INFO, msg); + } + info.model.setDirtyState(isDirty); + + IFile workspaceFile = FileBuffers.getWorkspaceFileAtLocation(info.buffer.getLocation()); + if (!isDirty && workspaceFile != null) { + info.model.resetSynchronizationStamp(workspaceFile); + } + } + } + } + + public void stateChangeFailed(IFileBuffer buffer) { + } + + public void stateChanging(IFileBuffer buffer) { + } + + public void stateValidationChanged(IFileBuffer buffer, boolean isStateValidated) { + } + + public void underlyingFileDeleted(IFileBuffer buffer) { + if (buffer instanceof ITextFileBuffer) { + if (Logger.DEBUG_TEXTBUFFERLIFECYCLE) { + Logger.log(Logger.INFO, "Deleted buffer: " + buffer.getLocation().toOSString() + " " + buffer); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + + public void underlyingFileMoved(IFileBuffer buffer, IPath path) { + if (buffer instanceof ITextFileBuffer) { + if (Logger.DEBUG_TEXTBUFFERLIFECYCLE) { + Logger.log(Logger.INFO, "Moved buffer from: " + buffer.getLocation().toOSString() + " " + buffer); //$NON-NLS-1$ //$NON-NLS-2$ + Logger.log(Logger.INFO, "Moved buffer to: " + path.toOSString() + " " + buffer); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + } + + private static FileBufferModelManager instance = new FileBufferModelManager(); + + public static FileBufferModelManager getInstance() { + return instance; + } + + static synchronized final void shutdown() { + FileBuffers.getTextFileBufferManager().removeFileBufferListener(instance.fFileBufferListener); + + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT || Logger.DEBUG_FILEBUFFERMODELLEAKS) { + IDocument[] danglingDocuments = (IDocument[]) instance.fDocumentMap.keySet().toArray(new IDocument[0]); + for (int i = 0; i < danglingDocuments.length; i++) { + DocumentInfo info = (DocumentInfo) instance.fDocumentMap.get(danglingDocuments[i]); + if (info.modelReferenceCount > 0) + System.err.println("LEAKED MODEL: " + info.buffer.getLocation() + " " + (info.model != null ? info.model.getId() : null)); //$NON-NLS-1$ //$NON-NLS-2$ + if (info.bufferReferenceCount > 0) + System.err.println("LEAKED BUFFER: " + info.buffer.getLocation() + " " + info.buffer.getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + + static synchronized final void startup() { + FileBuffers.getTextFileBufferManager().addFileBufferListener(getInstance().fFileBufferListener); + } + + // a map of IStructuredDocuments to DocumentInfo objects + Map fDocumentMap = null; + + FileBufferMapper fFileBufferListener = new FileBufferMapper(); + + FileBufferModelManager() { + super(); + fDocumentMap = new Hashtable(4); + } + + public String calculateId(IFile file) { + if (file == null) { + Exception iae = new IllegalArgumentException("can not calculate a model ID without an IFile"); //$NON-NLS-1$ + Logger.logException(iae); + return null; + } + + String id = null; + IPath path = file.getFullPath(); + if (path != null) { + /* + * The ID of models must be the same as the normalized paths + * stored in the underlying FileBuffers to retrieve them by common + * ID later on. We chose the FileBuffer normalized path over the + * previously used absolute IFile path because the buffers should + * already exist before we build a model and we can't retrieve a + * FileBuffer using the ID of a model that doesn't yet exist. + */ + id = FileBuffers.normalizeLocation(path).toString(); + } + return id; + + } + + + public String calculateId(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not calculate a model ID without a document reference"); //$NON-NLS-1$ + Logger.logException(iae); + return null; + } + + String id = null; + ITextFileBuffer buffer = getBuffer(document); + if (buffer != null) { + id = buffer.getLocation().toString(); + } + return id; + } + + /** + * Registers "interest" in a document, or rather the file buffer that + * backs it. Intentionally used to alter the reference count of the file + * buffer so it is not accidentally disposed of while we have a model open + * on top of it. + */ + public boolean connect(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not connect() without a document"); //$NON-NLS-1$ + Logger.logException(iae); + return false; + } + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if (info == null) + return false; + ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); + IPath bufferLocation = info.buffer.getLocation(); + boolean isOK = true; + try { + bufferManager.connect(bufferLocation, info.locationKind, null); + } + catch (CoreException e) { + Logger.logException(e); + isOK = false; + } + return isOK; + } + + URIResolver createURIResolver(ITextFileBuffer buffer) { + IPath location = buffer.getLocation(); + IFile workspaceFile = FileBuffers.getWorkspaceFileAtLocation(location); + URIResolver resolver = null; + if (workspaceFile != null) { + IProject project = workspaceFile.getProject(); + resolver = (URIResolver) project.getAdapter(URIResolver.class); + if (resolver == null) { + resolver = new CommonURIResolver(workspaceFile); + } + + String baseLocation = null; + if (workspaceFile.getLocation() != null) { + baseLocation = workspaceFile.getLocation().toString(); + } + if (baseLocation == null && workspaceFile.getLocationURI() != null) { + baseLocation = workspaceFile.getLocationURI().toString(); + } + if (baseLocation == null) { + baseLocation = workspaceFile.getFullPath().toString(); + } + resolver.setFileBaseLocation(baseLocation); + } + else { + resolver = new ExternalURIResolver(location); + } + return resolver; + } + + + IContentType detectContentType(IPath location) { + IContentType type = null; + + IResource resource = FileBuffers.getWorkspaceFileAtLocation(location); + if (resource != null) { + if (resource.getType() == IResource.FILE && resource.isAccessible()) { + IContentDescription d = null; + try { + // Optimized description lookup, might not succeed + d = ((IFile) resource).getContentDescription(); + if (d != null) { + type = d.getContentType(); + } + } + catch (CoreException e) { + // Should not be possible given the accessible and file + // type check above + } + if (type == null) { + type = Platform.getContentTypeManager().findContentTypeFor(resource.getName()); + } + } + } + else { + File file = FileBuffers.getSystemFileAtLocation(location); + if (file != null) { + InputStream input = null; + try { + input = new FileInputStream(file); + type = Platform.getContentTypeManager().findContentTypeFor(input, file.getName()); + } + catch (FileNotFoundException e) { + } + catch (IOException e) { + } + finally { + if (input != null) { + try { + input.close(); + } + catch (IOException e1) { + } + } + } + if (type == null) { + type = Platform.getContentTypeManager().findContentTypeFor(file.getName()); + } + } + } + if (type == null) { + type = Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT); + } + return type; + } + + /** + * Deregisters "interest" in a document, or rather the file buffer that + * backs it. Intentionally used to alter the reference count of the file + * buffer so that it knows it can safely be disposed of. + */ + public boolean disconnect(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not disconnect() without a document"); //$NON-NLS-1$ + Logger.logException(iae); + return false; + } + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if( info == null) + return false; + ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); + IPath bufferLocation = info.buffer.getLocation(); + boolean isOK = true; + try { + bufferManager.disconnect(bufferLocation, info.locationKind, null); + } + catch (CoreException e) { + Logger.logException(e); + isOK = false; + } + return isOK; + } + + public ITextFileBuffer getBuffer(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not get a buffer without a document reference"); //$NON-NLS-1$ + Logger.logException(iae); + return null; + } + + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if (info != null) + return info.buffer; + return null; + } + + String getContentTypeID(IDocument document) { + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if (info != null) + return info.contentTypeID; + return null; + } + + IStructuredModel getModel(File file) { + if (file == null) { + Exception iae = new IllegalArgumentException("can not get/create a model without a java.io.File"); //$NON-NLS-1$ + Logger.logException(iae); + return null; + } + + IStructuredModel model = null; + ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); + try { + IPath location = new Path(file.getAbsolutePath()); + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT) { + Logger.log(Logger.INFO, "FileBufferModelManager connecting to File " + location); //$NON-NLS-1$ + } + bufferManager.connect(location, LocationKind.LOCATION, getProgressMonitor()); + ITextFileBuffer buffer = bufferManager.getTextFileBuffer(location, LocationKind.LOCATION); + if (buffer != null) { + DocumentInfo info = (DocumentInfo) fDocumentMap.get(buffer.getDocument()); + if (info != null) { + /* + * Note: "info" being null at this point is a slight + * error. + * + * The connect call from above (or at some time earlier in + * the session) would have notified the FileBufferMapper + * of the creation of the corresponding text buffer and + * created the DocumentInfo object for + * IStructuredDocuments. + */ + info.locationKind = LocationKind.LOCATION; + info.selfConnected = true; + } + /* + * Check the document type. Although returning null for + * unknown documents would be fair, try to get a model if + * the document is at least a valid type. + */ + IDocument bufferDocument = buffer.getDocument(); + if (bufferDocument instanceof IStructuredDocument) { + model = getModel((IStructuredDocument) bufferDocument); + } + else { + /* + * 190768 - Quick diff marks do not disappear in the + * vertical ruler of JavaScript editor and + * + * 193805 - Changes are not thrown away when close + * with no save for files with no structured model + * associated with them (text files, javascript files, + * etc) in web project + */ + bufferManager.disconnect(location, LocationKind.IFILE, getProgressMonitor()); + } + } + } + catch (CoreException e) { + Logger.logException("Error getting model for " + file.getPath(), e); //$NON-NLS-1$ + } + return model; + } + + public IStructuredModel getModel(IFile file) { + if (file == null) { + Exception iae = new IllegalArgumentException("can not get/create a model without an IFile"); //$NON-NLS-1$ + Logger.logException(iae); + return null; + } + + IStructuredModel model = null; + ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); + try { + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT) { + Logger.log(Logger.INFO, "FileBufferModelManager connecting to IFile " + file.getFullPath()); //$NON-NLS-1$ + } + // see TextFileDocumentProvider#createFileInfo about why we use + // IFile#getFullPath + // here, not IFile#getLocation. + IPath location = file.getFullPath(); + if (location != null) { + bufferManager.connect(location, LocationKind.IFILE, getProgressMonitor()); + ITextFileBuffer buffer = bufferManager.getTextFileBuffer(location, LocationKind.IFILE); + if (buffer != null) { + DocumentInfo info = (DocumentInfo) fDocumentMap.get(buffer.getDocument()); + if (info != null) { + /* + * Note: "info" being null at this point is a slight + * error. + * + * The connect call from above (or at some time + * earlier in the session) would have notified the + * FileBufferMapper of the creation of the + * corresponding text buffer and created the + * DocumentInfo object for IStructuredDocuments. + */ + info.selfConnected = true; + info.locationKind = LocationKind.IFILE; + } + /* + * Check the document type. Although returning null for + * unknown documents would be fair, try to get a model if + * the document is at least a valid type. + */ + IDocument bufferDocument = buffer.getDocument(); + if (bufferDocument instanceof IStructuredDocument) { + model = getModel((IStructuredDocument) bufferDocument); + } + else { + /* + * 190768 - Quick diff marks do not disappear in the + * vertical ruler of JavaScript editor and + * + * 193805 - Changes are not thrown away when close + * with no save for files with no structured model + * associated with them (text files, javascript files, + * etc) in web project + */ + bufferManager.disconnect(location, LocationKind.IFILE, getProgressMonitor()); + } + } + } + } + catch (CoreException e) { + Logger.logException("Error getting model for " + file.getFullPath(), e); //$NON-NLS-1$ + } + return model; + } + + public IStructuredModel getModel(IStructuredDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not get/create a model without a document reference"); //$NON-NLS-1$ + Logger.logException(iae); + return null; + } + + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if (info != null && info.model == null) { + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT) { + Logger.log(Logger.INFO, "FileBufferModelManager creating model for " + info.buffer.getLocation() + " " + info.buffer.getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ + } + info.modelReferenceCount++; + + IStructuredModel model = null; + IModelHandler handler = ModelHandlerRegistry.getInstance().getHandlerForContentTypeId(info.contentTypeID); + IModelLoader loader = handler.getModelLoader(); + model = loader.createModel(document, info.buffer.getLocation().toString(), handler); + try { + info.model = model; + model.setId(info.buffer.getLocation().toString()); + // handler now set by loader, for now + // model.setModelHandler(handler); + if (model instanceof AbstractStructuredModel) { + ((AbstractStructuredModel) model).setContentTypeIdentifier(info.contentTypeID); + } + model.setResolver(createURIResolver(getBuffer(document))); + if (info.buffer.isDirty()) { + model.setDirtyState(true); + } + } + catch (ResourceInUse e) { + Logger.logException("attempted to create new model with existing ID", e); //$NON-NLS-1$ + model = null; + } + } + if (info != null) { + return info.model; + } + return null; + } + + /** + * @return + */ + private IProgressMonitor getProgressMonitor() { + return new NullProgressMonitor(); + } + + /** + * Will remove the entry corresponding to <code>document</code> if both + * there are no more buffer or model reference counts for <code>info</code> + * + * @param info the document info to check for reference counts + * @param document the key to remove from the document map if there are no more + * references + */ + private void checkReferenceCounts(DocumentInfo info, IDocument document) { + if (info.bufferReferenceCount == 0 && info.modelReferenceCount == 0) + fDocumentMap.remove(document); + } + + public boolean isExistingBuffer(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not check for an existing buffer without a document reference"); //$NON-NLS-1$ + Logger.logException(iae); + return false; + } + + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + return info != null; + } + + public void releaseModel(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not release a model without a document reference"); //$NON-NLS-1$ + Logger.logException(iae); + return; + } + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if (info != null) { + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT) { + Logger.log(Logger.INFO, "FileBufferModelManager noticed full release of model for " + info.buffer.getLocation() + " " + info.buffer.getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ + } + info.model = null; + info.modelReferenceCount--; + if (info.selfConnected) { + if (Logger.DEBUG_FILEBUFFERMODELMANAGEMENT) { + Logger.log(Logger.INFO, "FileBufferModelManager disconnecting from " + info.buffer.getLocation() + " " + info.buffer.getDocument()); //$NON-NLS-1$ //$NON-NLS-2$ + } + IPath location = info.buffer.getLocation(); + try { + FileBuffers.getTextFileBufferManager().disconnect(location, info.locationKind, getProgressMonitor()); + } + catch (CoreException e) { + Logger.logException("Error releasing model for " + location, e); //$NON-NLS-1$ + } + } + // [265899] + // In some scenarios, a model can be held onto after the editor has been disposed even if the lifecycle is + // maintained properly (e.g., an editor being closed before a DirtyRegionProcessor has a chance to complete). Because of this, + // the manager cannot be reliant upon the FileBufferMapper having the sole responsibility of the fDocumentMap cleanup + checkReferenceCounts(info, document); + } + } + + public void revert(IDocument document) { + if (document == null) { + Exception iae = new IllegalArgumentException("can not release a model without a document reference"); //$NON-NLS-1$ + Logger.logException(iae); + return; + } + DocumentInfo info = (DocumentInfo) fDocumentMap.get(document); + if (info == null) { + Logger.log(Logger.ERROR, "FileBufferModelManager was asked to revert a document but was not being managed"); //$NON-NLS-1$ + } + else { + // get path just for potential error message + IPath location = info.buffer.getLocation(); + try { + // ISSUE: in future, clients should provide progress monitor + info.buffer.revert(getProgressMonitor()); + } + catch (CoreException e) { + // ISSUE: shoudl we not be re-throwing CoreExceptions? Or + // not catch them at all? + Logger.logException("Error reverting model for " + location, e); //$NON-NLS-1$ + } + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/IExecutionDelegate.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/IExecutionDelegate.java new file mode 100644 index 0000000..63c5894 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/IExecutionDelegate.java
@@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ + +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.core.runtime.ISafeRunnable; + +/** + * An abstraction that allows even processing to be performed in a different + * context, e.g. a different Thread, if needed. + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IExecutionDelegate { + + void execute(ISafeRunnable runnable); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ILockable.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ILockable.java new file mode 100644 index 0000000..c8aa500 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ILockable.java
@@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ + +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.core.runtime.jobs.ILock; + +/** + * + * Not API: not to be used or implemented by clients. This is a special + * purpose interface to help guard some threading issues betweeen model and + * document. Will be changed soon. + * + */ + +public interface ILockable { + + ILock getLockObject(); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/JSPAwareAdapterFactory.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/JSPAwareAdapterFactory.java new file mode 100644 index 0000000..a9e2292 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/JSPAwareAdapterFactory.java
@@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.EmbeddedTypeHandler; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; + + +public interface JSPAwareAdapterFactory extends INodeAdapterFactory { + + void initializeWith(EmbeddedTypeHandler embeddedContentType); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/Logger.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/Logger.java new file mode 100644 index 0000000..f826867 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/Logger.java
@@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + + + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; + +/** + * Small convenience class to log messages to plugin's log file and also, if + * desired, the console. This class should only be used by classes in this + * plugin. Other plugins should make their own copy, with appropriate ID. + */ +public class Logger { + private static final String PLUGIN_ID = "org.eclipse.wst.sse.core"; //$NON-NLS-1$ + /** + * true if both platform and this plugin are in debug mode + */ + public static final boolean DEBUG = Platform.inDebugMode() && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/debug")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging adapter + * notification time + */ + public static final boolean DEBUG_ADAPTERNOTIFICATIONTIME = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/dom/adapter/notification/time")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging structured + * document + */ + public static final boolean DEBUG_DOCUMENT = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/structureddocument")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging file buffer + * model management + */ + public static final boolean DEBUG_FILEBUFFERMODELMANAGEMENT = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/filebuffers/modelmanagement")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging file buffer + * models not being released on shutdown + */ + public static final boolean DEBUG_FILEBUFFERMODELLEAKS = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/filebuffers/leaks")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging formatting + */ + public static final boolean DEBUG_FORMAT = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/format")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging text buffer + * lifecycle + */ + public static final boolean DEBUG_TEXTBUFFERLIFECYCLE = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/filebuffers/lifecycle")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging model + * lifecycle + */ + public static final boolean DEBUG_LIFECYCLE = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/structuredmodel/lifecycle")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging model state + */ + public static final boolean DEBUG_MODELSTATE = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/structuredmodel/state")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging model lock + * state + */ + public static final boolean DEBUG_MODELLOCK = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/structuredmodel/locks")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging model + * manager + */ + public static final boolean DEBUG_MODELMANAGER = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/structuredmodel/modelmanager")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + */ + public static final boolean DEBUG_TASKS = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + * content type detection + */ + public static final boolean DEBUG_TASKSCONTENTTYPE = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/detection")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + * jobs + */ + public static final boolean DEBUG_TASKSJOB = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/job")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + * overall performance + */ + public static final boolean DEBUG_TASKSOVERALLPERF = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/overalltime")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + * performance + */ + public static final boolean DEBUG_TASKSPERF = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/time")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + * preferences + */ + public static final boolean DEBUG_TASKSPREFS = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/preferences")); //$NON-NLS-1$ //$NON-NLS-2$ + /** + * true if platform and plugin are in debug mode and debugging task tags + * registry + */ + public static final boolean DEBUG_TASKSREGISTRY = DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.sse.core/tasks/registry")); //$NON-NLS-1$ //$NON-NLS-2$ + + /* + * Keep our own copy in case we want to add other severity levels + */ + public static final int OK = IStatus.OK; + public static final int INFO = IStatus.INFO; + public static final int WARNING = IStatus.WARNING; + public static final int ERROR = IStatus.ERROR; + public static final int OK_DEBUG = 200 + OK; + public static final int INFO_DEBUG = 200 + INFO; + public static final int WARNING_DEBUG = 200 + WARNING; + public static final int ERROR_DEBUG = 200 + ERROR; + + /** + * @return true if the platform is debugging + */ + private static boolean isDebugging() { + return Platform.inDebugMode(); + } + + /** + * Adds message to log. + * + * @param level + * severity level of the message (OK, INFO, WARNING, ERROR, + * @param message + * text to add to the log + * @param exception + * exception thrown + */ + private static void _log(int level, String message, Throwable exception) { + if (level == OK_DEBUG || level == INFO_DEBUG || level == WARNING_DEBUG || level == ERROR_DEBUG) { + if (!isDebugging()) + return; + } + int severity = IStatus.OK; + switch (level) { + case INFO_DEBUG : + case INFO : + severity = IStatus.INFO; + break; + case WARNING_DEBUG : + case WARNING : + severity = IStatus.WARNING; + break; + case ERROR_DEBUG : + case ERROR : + severity = IStatus.ERROR; + } + message = (message != null) ? message : ""; //$NON-NLS-1$ + Status statusObj = new Status(severity, PLUGIN_ID, severity, message, exception); + Bundle bundle = Platform.getBundle(PLUGIN_ID); + if (bundle != null) + Platform.getLog(bundle).log(statusObj); + } + + /** + * Write a message to the log with the given severity level + * + * @param level + * ERROR, WARNING, INFO, OK + * @param message + * message to add to the log + */ + public static void log(int level, String message) { + _log(level, message, null); + } + + /** + * Writes a message and exception to the log with the given severity level + * + * @param level + * ERROR, WARNING, INFO, OK + * @param message + * message to add to the log + * @param exception + * exception to add to the log + */ + public static void log(int level, String message, Throwable exception) { + _log(level, message, exception); + } + + /** + * Writes the exception as an error in the log along with an accompanying + * message + * + * @param message + * message to add to the log + * @param exception + * exception to add to the log + */ + public static void logException(String message, Throwable exception) { + _log(IStatus.ERROR, message, exception); + } + + /** + * Writes the exception as an error in the log + * + * @param exception + * exception to add to the log + */ + public static void logException(Throwable exception) { + _log(IStatus.ERROR, exception.getMessage(), exception); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ModelManagerPluginRegistryReader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ModelManagerPluginRegistryReader.java new file mode 100644 index 0000000..2b9025f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ModelManagerPluginRegistryReader.java
@@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Vector; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IDocumentTypeHandler; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; +import org.osgi.framework.Bundle; + + +public class ModelManagerPluginRegistryReader { + private static ModelManagerPluginRegistryReader reader = null; + + public synchronized static ModelManagerPluginRegistryReader getInstance() { + if (reader == null) { + reader = new ModelManagerPluginRegistryReader(); + } + return reader; + } + + protected final String ATTR_ADAPTERKEY = "adapterKeyClass"; //$NON-NLS-1$ + protected final String ATTR_CLASS = "class"; //$NON-NLS-1$ + protected final String ATTR_CONTENTTYPE = "type"; //$NON-NLS-1$ + protected final String ATTR_REGISTERADAPTER = "registerAdapters"; //$NON-NLS-1$ + + protected final String EXTENSION_POINT_ID = "adaptOnCreateFactory"; //$NON-NLS-1$ + protected final String TAG_NAME = "AdaptOnCreateFactory"; //$NON-NLS-1$ + + /** + * XMLEditorPluginRegistryReader constructor comment. + */ + protected ModelManagerPluginRegistryReader() { + super(); + } + + public List getFactories(IDocumentTypeHandler handler) { + return loadRegistry(handler.getId()); + } + + public List getFactories(String type) { + return loadRegistry(type); + } + + protected INodeAdapterFactory loadFactoryFromConfigurationElement(IConfigurationElement element, Object requesterType) { + INodeAdapterFactory factory = null; + if (element.getName().equals(TAG_NAME)) { + String contentType = element.getAttribute(ATTR_CONTENTTYPE); + if (!contentType.equals(requesterType)) + return null; + String className = element.getAttribute(ATTR_CLASS); + String adapterKeyClass = element.getAttribute(ATTR_ADAPTERKEY); + String registerAdapters = element.getAttribute(ATTR_REGISTERADAPTER); + + // if className is null, then no one defined the extension point + // for adapter factories + if (className != null) { + String name = element.getDeclaringExtension().getNamespace(); + Bundle bundle = null; + try { + bundle = Platform.getBundle(name); + } + catch (Exception e) { + // if an error occurs here, its probably that the plugin + // could not be found/loaded + Logger.logException("Could not find bundle: " + name, e); //$NON-NLS-1$ + + } + if (bundle != null) { + boolean useExtendedConstructor = false; + boolean doRegisterAdapters = false; + Object adapterKey = null; + + if (registerAdapters != null && registerAdapters.length() > 0 && Boolean.valueOf(registerAdapters).booleanValue()) { + doRegisterAdapters = true; + } + if (adapterKeyClass != null) { + try { + Class aClass = null; + // aClass = classLoader != null ? + // classLoader.loadClass(adapterKeyClass) : + // Class.forName(adapterKeyClass); + if (bundle.getState() != Bundle.UNINSTALLED) { + aClass = bundle.loadClass(adapterKeyClass); + } + else { + aClass = Class.forName(adapterKeyClass); + } + if (aClass != null) { + useExtendedConstructor = true; + adapterKey = aClass; + } + else { + adapterKey = adapterKeyClass; + } + } + catch (Exception anyErrors) { + adapterKey = adapterKeyClass; + } + } + + try { + Class theClass = null; + // Class theClass = classLoader != null ? + // classLoader.loadClass(className) : + // Class.forName(className); + if (bundle.getState() != Bundle.UNINSTALLED) { + theClass = bundle.loadClass(className); + } + else { + theClass = Class.forName(className); + } + if (useExtendedConstructor) { + java.lang.reflect.Constructor[] ctors = theClass.getConstructors(); + for (int i = 0; i < ctors.length; i++) { + Class[] paramTypes = ctors[i].getParameterTypes(); + if (ctors[i].isAccessible() && paramTypes.length == 2 && paramTypes[0].equals(Object.class) && paramTypes[1].equals(boolean.class)) { + try { + factory = (INodeAdapterFactory) ctors[i].newInstance(new Object[]{adapterKey, new Boolean(doRegisterAdapters)}); + } + catch (IllegalAccessException e) { + // log for now, unless we find reason + // not to + Logger.log(Logger.INFO, e.getMessage()); + } + catch (IllegalArgumentException e) { + // log for now, unless we find reason + // not to + Logger.log(Logger.INFO, e.getMessage()); + } + catch (InstantiationException e) { + // log for now, unless we find reason + // not to + Logger.log(Logger.INFO, e.getMessage()); + } + catch (InvocationTargetException e) { + // log for now, unless we find reason + // not to + Logger.log(Logger.INFO, e.getMessage()); + } + catch (ExceptionInInitializerError e) { + // log or now, unless we find reason + // not to + Logger.log(Logger.INFO, e.getMessage()); + } + } + } + } + if (factory == null) { + factory = (INodeAdapterFactory) element.createExecutableExtension(ATTR_CLASS); + } + } + catch (ClassNotFoundException e) { + // log or now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + catch (CoreException e) { + // log or now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + } + } + } + return factory; + } + + protected List loadRegistry(Object contentType) { + List factoryList = new Vector(); + IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); + IExtensionPoint point = extensionRegistry.getExtensionPoint(SSECorePlugin.ID, EXTENSION_POINT_ID); + if (point != null) { + IConfigurationElement[] elements = point.getConfigurationElements(); + for (int i = 0; i < elements.length; i++) { + INodeAdapterFactory factory = loadFactoryFromConfigurationElement(elements[i], contentType); + if (factory != null) + factoryList.add(factory); + } + } + return factoryList; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NoCancelProgressMonitor.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NoCancelProgressMonitor.java new file mode 100644 index 0000000..c9f129f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NoCancelProgressMonitor.java
@@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ + +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.core.runtime.NullProgressMonitor; + + +public class NoCancelProgressMonitor extends NullProgressMonitor { + + + public NoCancelProgressMonitor() { + super(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.IProgressMonitor#isCanceled() + */ + public boolean isCanceled() { + + return false; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NotImplementedException.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NotImplementedException.java new file mode 100644 index 0000000..3a0b985 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NotImplementedException.java
@@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + + +public class NotImplementedException extends RuntimeException { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + public NotImplementedException() { + super(); + } + + public NotImplementedException(String message) { + super(message); + } + + public NotImplementedException(String message, Throwable cause) { + super(message, cause); + } + + public NotImplementedException(Throwable cause) { + super(cause); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NullMemento.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NullMemento.java new file mode 100644 index 0000000..9da4305 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/NullMemento.java
@@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.encoding.NonContentBasedEncodingRules; + + + +/** + * This class can be used in place of an EncodingMemento (its super class), + * when there is not in fact ANY encoding information. For example, when a + * structuredDocument is created directly from a String + */ +public class NullMemento extends EncodingMemento { + /** + * + */ + public NullMemento() { + super(); + String defaultCharset = NonContentBasedEncodingRules.useDefaultNameRules(null); + setJavaCharsetName(defaultCharset); + setAppropriateDefault(defaultCharset); + setDetectedCharsetName(null); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/PropagatingAdapter.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/PropagatingAdapter.java new file mode 100644 index 0000000..401c484 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/PropagatingAdapter.java
@@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import java.util.List; + +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; + +public interface PropagatingAdapter extends INodeAdapter { + + void addAdaptOnCreateFactory(INodeAdapterFactory factory); + + List getAdaptOnCreateFactories(); + + /** + * This method should be called immediately after adding a factory, + * typically on the document (top level) node, so all nodes can be + * adapted, if needed. This is needed for those occasions when a factory + * is addeded after some nodes may have already been created at the time + * the factory is added. + */ + void initializeForFactory(INodeAdapterFactory factory, INodeNotifier node); + + // dmw: should have getFactoryFor? + void release(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/PropagatingAdapterFactory.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/PropagatingAdapterFactory.java new file mode 100644 index 0000000..70c84da --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/PropagatingAdapterFactory.java
@@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + + + +import java.util.ArrayList; + +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; + + + +public interface PropagatingAdapterFactory extends INodeAdapterFactory { + + void addContributedFactories(INodeAdapterFactory factory); + + void setContributedFactories(ArrayList list); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java new file mode 100644 index 0000000..4546839 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java
@@ -0,0 +1,50 @@ +/********************************************************************** + * Copyright (c) 2005, 2010 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 - Initial API and implementation + **********************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.osgi.util.NLS; + +/** + * Strings used by SSE Core + * + * @plannedfor 1.0 + */ +public class SSECoreMessages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.wst.sse.core.internal.SSECorePluginResources";//$NON-NLS-1$ + + static { + // load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, SSECoreMessages.class); + } + + private SSECoreMessages() { + // cannot create new instance + } + + public static String A_model_s_id_can_not_be_nu_EXC_; + public static String Program_Error__ModelManage_EXC_; + public static String Original_Error__UI_; + public static String Text_Change_UI_; + public static String TaskScanner_0; + public static String TaskScanningJob_0; + public static String TaskScanningJob_1; + public static String Migrate_Charset; + + public static String IndexManager_0_starting; + public static String IndexManager_0_starting_1; + public static String IndexManager_0_Indexing_1_Files; + public static String IndexManager_processing_deferred_resource_changes; + public static String IndexManager_Processing_entire_workspace_for_the_first_time; + public static String IndexManager_0_Processing_entire_workspace_for_the_first_time; + public static String IndexManager_processing_recent_resource_changes; + public static String IndexManager_0_resources_to_go_1; + public static String IndexManager_Waiting_for_0; + public static String IndexManager_0_Processing_resource_events; +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePlugin.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePlugin.java new file mode 100644 index 0000000..15e32bb --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePlugin.java
@@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal; + +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Preferences; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.encoding.CommonEncodingPreferenceNames; +import org.eclipse.wst.sse.core.internal.modelhandler.ModelHandlerRegistry; +import org.eclipse.wst.sse.core.internal.preferences.CommonModelPreferenceNames; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.tasks.TaskScanningScheduler; +import org.osgi.framework.BundleContext; + + +/** + * SSE Core Plugin. + */ +public class SSECorePlugin extends Plugin { + static SSECorePlugin instance = null; + + public static final String ID = "org.eclipse.wst.sse.core"; //$NON-NLS-1$ + + public static SSECorePlugin getDefault() { + return instance; + } + + public SSECorePlugin() { + super(); + instance = this; + } + + /** + * Set default non-UI + */ + protected void initializeDefaultPluginPreferences() { + Preferences prefs = getDefault().getPluginPreferences(); + // set model preference defaults + + prefs.setDefault(CommonEncodingPreferenceNames.USE_3BYTE_BOM_WITH_UTF8, false); + + prefs.setDefault(CommonModelPreferenceNames.TASK_TAG_ENABLE, false); + prefs.setDefault(CommonModelPreferenceNames.TASK_TAG_TAGS, "TODO,FIXME,XXX"); //$NON-NLS-1$ + prefs.setDefault(CommonModelPreferenceNames.TASK_TAG_PRIORITIES, "1,2,1"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + savePluginPreferences(); + + TaskScanningScheduler.shutdown(); + + FileBufferModelManager.shutdown(); + + super.stop(context); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.Plugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + + // initialize FileBuffer handling + FileBufferModelManager.startup(); + + /** + * If the user starts the workbench with + * -Dorg.eclipse.wst.sse.core.taskscanner=off, the scanner should be + * disabled + */ + String scan = System.getProperty("org.eclipse.wst.sse.core.taskscanner"); //$NON-NLS-1$ + if (scan == null || !scan.equalsIgnoreCase("off")) { //$NON-NLS-1$ + TaskScanningScheduler.startup(); + } + } + + /** + * @deprecated + */ + public ModelHandlerRegistry getModelHandlerRegistry() { + return ModelHandlerRegistry.getInstance(); + } + + /** + * @deprecated - use StructuredModelManager.getModelManager(); + */ + public IModelManager getModelManager() { + return StructuredModelManager.getModelManager(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties new file mode 100644 index 0000000..971c0f7 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties
@@ -0,0 +1,32 @@ +############################################################################### +# Copyright (c) 2001, 2010 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 +# Jens Lukowski/Innoopract - initial renaming/restructuring +# +############################################################################### +A_model_s_id_can_not_be_nu_EXC_=A model's id can not be null +Program_Error__ModelManage_EXC_=Program Error: ModelManagerImpl::saveModel. Model should be in the cache +Original_Error__UI_=Original Error: +Text_Change_UI_=Text Change +TaskScanner_0=Scanning for Tasks +TaskScanningJob_0=Scanning +TaskScanningJob_1=Errors while detecting Tasks +############################################################################### +Migrate_Charset=Migrate Charset + +IndexManager_0_starting={0}: Starting +IndexManager_0_starting_1={0}: Starting: {1} +IndexManager_0_Indexing_1_Files={0}: Indexing {1} Files +IndexManager_processing_deferred_resource_changes=Processing deferred resource changes +IndexManager_Processing_entire_workspace_for_the_first_time=Processing entire workspace for the first time +IndexManager_0_Processing_entire_workspace_for_the_first_time={0}: Processing entire workspace for the first time +IndexManager_processing_recent_resource_changes=Processing recent resource changes +IndexManager_0_resources_to_go_1={0} resources to index: {1} +IndexManager_Waiting_for_0=Waiting for {0} +IndexManager_0_Processing_resource_events={0}: Processing Resource Events \ No newline at end of file
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/AbstractStructuredCleanupProcessor.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/AbstractStructuredCleanupProcessor.java new file mode 100644 index 0000000..f525d5f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/AbstractStructuredCleanupProcessor.java
@@ -0,0 +1,464 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; +import java.util.Vector; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension4; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.format.IStructuredFormatProcessor; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.w3c.dom.Attr; +import org.w3c.dom.Node; + + +public abstract class AbstractStructuredCleanupProcessor implements IStructuredCleanupProcessor { + public boolean refreshCleanupPreferences = true; // special flag for JUnit + + // tests to skip refresh + // of cleanup preferences + // when it's set to false + + public String cleanupContent(String input) throws IOException, CoreException { + IStructuredModel structuredModel = null; + InputStream inputStream = null; + try { + // setup structuredModel + inputStream = new ByteArrayInputStream(input.getBytes("UTF8")); //$NON-NLS-1$ + String id = inputStream.toString() + getContentType(); + structuredModel = StructuredModelManager.getModelManager().getModelForRead(id, inputStream, null); + + // cleanup + cleanupModel(structuredModel, 0, structuredModel.getStructuredDocument().getLength()); + + // return output + return structuredModel.getStructuredDocument().get(); + } finally { + ensureClosed(null, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public String cleanupContent(String input, int start, int length) throws IOException, CoreException { + IStructuredModel structuredModel = null; + InputStream inputStream = null; + try { + // setup structuredModel + inputStream = new ByteArrayInputStream(input.getBytes("UTF8")); //$NON-NLS-1$ + String id = inputStream.toString() + getContentType(); + structuredModel = StructuredModelManager.getModelManager().getModelForRead(id, inputStream, null); + + // cleanup + cleanupModel(structuredModel, start, length); + + // return output + return structuredModel.getStructuredDocument().get(); + } finally { + ensureClosed(null, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public void cleanupDocument(IDocument document) throws IOException, CoreException { + if (document == null) + return; + + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if model + // changed. + structuredModel = StructuredModelManager.getModelManager().getExistingModelForEdit(document); + + // cleanup + cleanupModel(structuredModel); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + + public void cleanupDocument(IDocument document, int start, int length) throws IOException, CoreException { + if (document == null) + return; + + if (start >= 0 && length >= 0 && start + length <= document.getLength()) { + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if + // model changed. + structuredModel = StructuredModelManager.getModelManager().getExistingModelForEdit(document); + + // cleanup + cleanupModel(structuredModel, start, length); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + } + + public void cleanupFile(IFile file) throws IOException, CoreException { + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + structuredModel = StructuredModelManager.getModelManager().getModelForRead(file); + + // cleanup + cleanupModel(structuredModel, 0, structuredModel.getStructuredDocument().getLength()); + + // save output to file + // outputStream = new + // FileOutputStream(file.getLocation().toString()); + structuredModel.save(file); + } finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public void cleanupFile(IFile file, int start, int length) throws IOException, CoreException { + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + structuredModel = StructuredModelManager.getModelManager().getModelForRead(file); + + // cleanup + cleanupModel(structuredModel, start, length); + + // save output to file + // outputStream = new + // FileOutputStream(file.getLocation().toString()); + structuredModel.save(file); + } finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public void cleanupFileName(String fileName) throws IOException, CoreException { + IStructuredModel structuredModel = null; + InputStream inputStream = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + inputStream = new FileInputStream(fileName); + structuredModel = StructuredModelManager.getModelManager().getModelForRead(fileName, inputStream, null); + + // cleanup + cleanupModel(structuredModel, 0, structuredModel.getStructuredDocument().getLength()); + + // save output to file + // outputStream = new FileOutputStream(fileName); + structuredModel.save(); + } finally { + // ensureClosed(outputStream, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public void cleanupFileName(String fileName, int start, int length) throws IOException, CoreException { + IStructuredModel structuredModel = null; + InputStream inputStream = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + inputStream = new FileInputStream(fileName); + structuredModel = StructuredModelManager.getModelManager().getModelForRead(fileName, inputStream, null); + + // cleanup + cleanupModel(structuredModel, start, length); + + // save output to file + // outputStream = new FileOutputStream(fileName); + structuredModel.save(); + } finally { + // ensureClosed(outputStream, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public void cleanupModel(IStructuredModel structuredModel) { + + int start = 0; + int length = structuredModel.getStructuredDocument().getLength(); + cleanupModel(structuredModel, start, length); + } + + public void cleanupModel(IStructuredModel structuredModel, int start, int length) { + + if (structuredModel != null) { + if ((start >= 0) && (length <= structuredModel.getStructuredDocument().getLength())) { + Vector activeNodes = getActiveNodes(structuredModel, start, length); + if (activeNodes.size() > 0) { + Node firstNode = (Node) activeNodes.firstElement(); + Node lastNode = (Node) activeNodes.lastElement(); + boolean done = false; + Node eachNode = firstNode; + Node nextNode = null; + + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=123621 + // if doing any sort of cleanup, set up rewrite session/modelchanged + IDocumentExtension4 docExt4 = null; + if (structuredModel.getStructuredDocument() instanceof IDocumentExtension4) { + docExt4 = (IDocumentExtension4) structuredModel.getStructuredDocument(); + } + DocumentRewriteSession rewriteSession = null; + + try { + // whenever formatting model, fire + // abouttochange/modelchanged + structuredModel.aboutToChangeModel(); + rewriteSession = (docExt4 == null || docExt4.getActiveRewriteSession() != null) ? null : docExt4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); + + while (!done) { + // update "done" + done = (eachNode == lastNode); + + // get next sibling before cleanup because eachNode + // may + // be deleted, + // for example when it's an empty text node + nextNode = eachNode.getNextSibling(); + + // cleanup selected node(s) + cleanupNode(eachNode); + + // update each node + if (nextNode != null && nextNode.getParentNode() == null) + // nextNode is deleted during cleanup + eachNode = eachNode.getNextSibling(); + else + eachNode = nextNode; + + // This should not be needed, but just in case + // something went wrong with with eachNode. + // We don't want an infinite loop here. + if (eachNode == null) + done = true; + } + + // format source + if (getFormatSourcePreference(structuredModel)) { + // format the document + IStructuredFormatProcessor formatProcessor = getFormatProcessor(); + formatProcessor.formatModel(structuredModel); + } + } + finally { + // we need two finally's, just in case first fails + try { + if ((docExt4 != null) && (rewriteSession != null)) + docExt4.stopRewriteSession(rewriteSession); + } + finally { + // always make sure to fire changedmodel when done + structuredModel.changedModel(); + } + } + } + } + } + } + + public void cleanupNode(Node node) { + if (node != null) { + Node cleanupNode = node; + + // cleanup the owner node if it's an attribute node + if (cleanupNode.getNodeType() == Node.ATTRIBUTE_NODE) + cleanupNode = ((Attr) cleanupNode).getOwnerElement(); + + // refresh cleanup preferences before getting cleanup handler + if (refreshCleanupPreferences) + refreshCleanupPreferences(); + + // get cleanup handler + IStructuredCleanupHandler cleanupHandler = getCleanupHandler(cleanupNode); + if (cleanupHandler != null) { + // cleanup each node + cleanupHandler.cleanup(cleanupNode); + } + } + } + + protected void convertLineDelimiters(IDocument document, String newDelimiter) { + final int lineCount = document.getNumberOfLines(); + Map partitioners = TextUtilities.removeDocumentPartitioners(document); + try { + for (int i = 0; i < lineCount; i++) { + final String delimiter = document.getLineDelimiter(i); + if (delimiter != null && delimiter.length() > 0 && !delimiter.equals(newDelimiter)) { + IRegion region = document.getLineInformation(i); + document.replace(region.getOffset() + region.getLength(), delimiter.length(), newDelimiter); + } + } + } catch (BadLocationException e) { + Logger.logException(e); + } finally { + TextUtilities.addDocumentPartitioners(document, partitioners); + } + } + + protected void ensureClosed(OutputStream outputStream, InputStream inputStream) { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + Logger.logException(e); // hopeless + } + try { + if (outputStream != null) { + outputStream.close(); + } + } catch (IOException e) { + Logger.logException(e); // hopeless + } + } + + protected Vector getActiveNodes(IStructuredModel structuredModel, int startNodeOffset, int length) { + Vector activeNodes = new Vector(); + + if (structuredModel != null) { + Node startNode = (Node) structuredModel.getIndexedRegion(startNodeOffset); + Node endNode = (Node) structuredModel.getIndexedRegion(startNodeOffset + length); + + // make sure it's an non-empty document + if (startNode != null) { + while (isSiblingOf(startNode, endNode) == false) { + if (endNode != null) + endNode = endNode.getParentNode(); + if (endNode == null) { + startNode = startNode.getParentNode(); + endNode = (Node) structuredModel.getIndexedRegion(startNodeOffset + length); + } + } + + while (startNode != endNode) { + activeNodes.addElement(startNode); + startNode = startNode.getNextSibling(); + } + if (startNode != null) + activeNodes.addElement(startNode); + } + } + + return activeNodes; + } + + abstract protected IStructuredCleanupHandler getCleanupHandler(Node node); + + abstract protected String getContentType(); + + protected boolean getConvertEOLCodesPreference(IStructuredModel structuredModel) { + + boolean convertEOLCodes = true; + IStructuredCleanupHandler cleanupHandler = getCleanupHandler((Node) structuredModel.getIndexedRegion(0)); + if (cleanupHandler != null) { + IStructuredCleanupPreferences cleanupPreferences = cleanupHandler.getCleanupPreferences(); + convertEOLCodes = cleanupPreferences.getConvertEOLCodes(); + } + return convertEOLCodes; + } + + protected String getEOLCodePreference(IStructuredModel structuredModel) { + + IScopeContext[] scopeContext = new IScopeContext[]{new InstanceScope()}; + String eolCode = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); + + IStructuredCleanupHandler cleanupHandler = getCleanupHandler((Node) structuredModel.getIndexedRegion(0)); + if (cleanupHandler != null) { + IStructuredCleanupPreferences cleanupPreferences = cleanupHandler.getCleanupPreferences(); + eolCode = cleanupPreferences.getEOLCode(); + } + return eolCode; + } + + abstract protected IStructuredFormatProcessor getFormatProcessor(); + + protected boolean getFormatSourcePreference(IStructuredModel structuredModel) { + + boolean formatSource = true; + IStructuredCleanupHandler cleanupHandler = getCleanupHandler((Node) structuredModel.getIndexedRegion(0)); + if (cleanupHandler != null) { + IStructuredCleanupPreferences cleanupPreferences = cleanupHandler.getCleanupPreferences(); + formatSource = cleanupPreferences.getFormatSource(); + } + return formatSource; + } + + protected boolean isSiblingOf(Node node, Node endNode) { + if (endNode == null) { + return true; + } else { + Node siblingNode = node; + while (siblingNode != null) { + if (siblingNode == endNode) + return true; + else + siblingNode = siblingNode.getNextSibling(); + } + return false; + } + } + + abstract protected void refreshCleanupPreferences(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupHandler.java new file mode 100644 index 0000000..4f98956 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupHandler.java
@@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +import org.w3c.dom.Node; + +public interface IStructuredCleanupHandler { + Node cleanup(Node node); + + IStructuredCleanupPreferences getCleanupPreferences(); + + void setCleanupPreferences(IStructuredCleanupPreferences cleanupPreferences); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupPreferences.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupPreferences.java new file mode 100644 index 0000000..7f5058d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupPreferences.java
@@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +import org.eclipse.core.runtime.Preferences; + +public interface IStructuredCleanupPreferences { + + int getAttrNameCase(); + + boolean getCompressEmptyElementTags(); + + boolean getConvertEOLCodes(); + + String getEOLCode(); + + boolean getFormatSource(); + + boolean getInsertMissingTags(); + + boolean getInsertRequiredAttrs(); + + boolean getQuoteAttrValues(); + + int getTagNameCase(); + + void setAttrNameCase(int attrNameCase); + + void setCompressEmptyElementTags(boolean compressEmptyElementTags); + + void setConvertEOLCodes(boolean convertEOLCodes); + + void setEOLCode(String EOLCode); + + void setFormatSource(boolean formatSource); + + void setInsertMissingTags(boolean insertMissingTags); + + void setInsertRequiredAttrs(boolean insertRequiredAttrs); + + void setPreferences(Preferences preferences); + + void setQuoteAttrValues(boolean quoteAttrValues); + + void setTagNameCase(int tagNameCase); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupProcessor.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupProcessor.java new file mode 100644 index 0000000..27bce4c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/IStructuredCleanupProcessor.java
@@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +import java.io.IOException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.w3c.dom.Node; + +/** + * This interface and related classes are 'internal' and should not + * be treated as API, even though used across components in WTP. + * Consider it a work in progress. + */ + +public interface IStructuredCleanupProcessor { + /** + * This form of the CleanupProcessor takes an input string as input, + * creates an InputStream from the input string, create a temporary model + * of the content type specified, cleanups the whole model, then returns + * the cleaned up input string. + */ + String cleanupContent(String content) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes an input string as input, + * creates an InputStream from the input string, create a temporary model + * of the content type specified, cleanups the model within start and + * length, then returns the cleaned up input string. + */ + String cleanupContent(String content, int start, int length) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes an IDocument as input, creates + * a temporary model of content type calculated using the IDocument's file + * extension, cleanups the whole model, then releases the model. + */ + void cleanupDocument(IDocument document) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes an IDocument as input, creates + * a temporary model of content type calculated using the IDocument's file + * extension, cleanups the model within start and length, then releases + * the model. + */ + void cleanupDocument(IDocument document, int start, int length) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes an IFile as input, creates a + * temporary model of content type calculated using the IFile's file + * extension, cleanups the whole model, then releases the model. The IFile + * is updated when the last reference of the model is released in the + * model manager. + */ + void cleanupFile(IFile file) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes an IFile as input, creates a + * temporary model of content type calculated using the IFile's file + * extension, cleanups the model within start and length, then releases + * the model. The IFile is updated when the last reference of the model is + * released in the model manager. + */ + void cleanupFile(IFile file, int start, int length) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes a file name as input,creates an + * InputStream from the file, create a temporary model of content type + * calculated using the file name's file extension, cleanups the whole + * model, then releases the model. The file is updated when the last + * reference of the model is released in the model manager. + */ + void cleanupFileName(String fileName) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes a file name as input,creates an + * InputStream from the file, create a temporary model of content type + * calculated using the file name's file extension, cleanups the model + * within start and length, then releases the model. The file is updated + * when the last reference of the model is released in the model manager. + */ + void cleanupFileName(String fileName, int start, int length) throws IOException, CoreException; + + /** + * This form of the CleanupProcessor takes a model as input, and cleanups + * the whole model. + */ + void cleanupModel(IStructuredModel structuredModel); + + /** + * This form of the CleanupProcessor takes a model as input, and cleanups + * the model within start and length. + */ + void cleanupModel(IStructuredModel structuredModel, int start, int length); + + /** + * This form of the CleanupProcessor takes a node as input, and formats + * the node and all its children. + */ + void cleanupNode(Node node); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredCleanupPreferences.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredCleanupPreferences.java new file mode 100644 index 0000000..a834418 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredCleanupPreferences.java
@@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +import org.eclipse.core.runtime.Preferences; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; + + +public class StructuredCleanupPreferences implements IStructuredCleanupPreferences { + private int fAttrNameCase; + private boolean fCompressEmptyElementTags; + private boolean fConvertEOLCodes; + private String fEOLCode; + private boolean fFormatSource; + private boolean fInsertMissingTags; + private boolean fInsertRequiredAttrs; + //private IPreferenceStore fPreferenceStore = null; + private Preferences fPreferences = null; + private boolean fQuoteAttrValues; + + private int fTagNameCase; + + public int getAttrNameCase() { + + return fAttrNameCase; + } + + public boolean getCompressEmptyElementTags() { + + return fCompressEmptyElementTags; + } + + public boolean getConvertEOLCodes() { + + return fConvertEOLCodes; + } + + public String getEOLCode() { + + return fEOLCode; + } + + public boolean getFormatSource() { + + return fFormatSource; + } + + public boolean getInsertMissingTags() { + + return fInsertMissingTags; + } + + public boolean getInsertRequiredAttrs() { + + return fInsertRequiredAttrs; + } + + public Preferences getPreferences() { + + if (fPreferences == null) { + fPreferences = SSECorePlugin.getDefault().getPluginPreferences(); + } + return fPreferences; + } + + public boolean getQuoteAttrValues() { + + return fQuoteAttrValues; + } + + public int getTagNameCase() { + + return fTagNameCase; + } + + public void setAttrNameCase(int attrNameCase) { + + fAttrNameCase = attrNameCase; + } + + public void setCompressEmptyElementTags(boolean compressEmptyElementTags) { + + fCompressEmptyElementTags = compressEmptyElementTags; + } + + public void setConvertEOLCodes(boolean convertEOLCodes) { + + fConvertEOLCodes = convertEOLCodes; + } + + public void setEOLCode(String EOLCode) { + + fEOLCode = EOLCode; + } + + public void setFormatSource(boolean formatSource) { + + fFormatSource = formatSource; + } + + public void setInsertMissingTags(boolean insertMissingTags) { + + fInsertMissingTags = insertMissingTags; + } + + public void setInsertRequiredAttrs(boolean insertRequiredAttrs) { + + fInsertRequiredAttrs = insertRequiredAttrs; + } + + public void setPreferences(Preferences prefs) { + + fPreferences = prefs; + } + + public void setQuoteAttrValues(boolean quoteAttrValues) { + + fQuoteAttrValues = quoteAttrValues; + } + + public void setTagNameCase(int tagNameCase) { + + fTagNameCase = tagNameCase; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredContentCleanupHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredContentCleanupHandler.java new file mode 100644 index 0000000..6c06ca0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredContentCleanupHandler.java
@@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +public interface StructuredContentCleanupHandler { + + IStructuredCleanupProcessor getCleanupProcessor(String contentType); + + void setCleanupProcessor(IStructuredCleanupProcessor cleanupProcessor, String contentType); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredContentCleanupHandlerImpl.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredContentCleanupHandlerImpl.java new file mode 100644 index 0000000..27a6246 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/cleanup/StructuredContentCleanupHandlerImpl.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.cleanup; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.text.Assert; + +public class StructuredContentCleanupHandlerImpl implements StructuredContentCleanupHandler { + protected Map fCleanupProcessors; + + public IStructuredCleanupProcessor getCleanupProcessor(String contentType) { + Assert.isNotNull(contentType); + + if (fCleanupProcessors == null) + return null; + + return (IStructuredCleanupProcessor) fCleanupProcessors.get(contentType); + } + + public void setCleanupProcessor(IStructuredCleanupProcessor cleanupProcessor, String contentType) { + Assert.isNotNull(contentType); + + if (fCleanupProcessors == null) + fCleanupProcessors = new HashMap(); + + if (fCleanupProcessors == null) + fCleanupProcessors.remove(contentType); + else + fCleanupProcessors.put(contentType, cleanupProcessor); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/AbstractDocumentLoader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/AbstractDocumentLoader.java new file mode 100644 index 0000000..ea9959e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/AbstractDocumentLoader.java
@@ -0,0 +1,438 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.document; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension3; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; +import org.eclipse.wst.sse.core.internal.encoding.CodedReaderCreator; +import org.eclipse.wst.sse.core.internal.encoding.ContentTypeEncodingPreferences; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.exceptions.MalformedInputExceptionWithDetail; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredPartitioning; + + + +/** + * This class reads a file and creates an Structured Model. + */ +public abstract class AbstractDocumentLoader implements IDocumentLoader { + + private CodedReaderCreator fCodedReaderCreator; + protected IDocumentCharsetDetector fDocumentEncodingDetector; + // private boolean fPropertiesObtained; + + protected EncodingMemento fEncodingMemento; + protected Reader fFullPreparedReader; + + /** + * AbstractLoader constructor also initializes encoding converter/mapper + */ + public AbstractDocumentLoader() { + super(); + } + + protected final StringBuffer convertLineDelimiters(StringBuffer allTextBuffer, String lineDelimiterToUse) { + // TODO: avoid use of String instance + String allText = allTextBuffer.toString(); + IDocument tempDoc = new Document(allText); + if (lineDelimiterToUse == null) + lineDelimiterToUse = System.getProperty("line.separator"); //$NON-NLS-1$ + StringBuffer newText = new StringBuffer(); + int lineCount = tempDoc.getNumberOfLines(); + for (int i = 0; i < lineCount; i++) { + try { + org.eclipse.jface.text.IRegion lineInfo = tempDoc.getLineInformation(i); + int lineStartOffset = lineInfo.getOffset(); + int lineLength = lineInfo.getLength(); + int lineEndOffset = lineStartOffset + lineLength; + newText.append(allText.substring(lineStartOffset, lineEndOffset)); + if ((i < lineCount - 1) && (tempDoc.getLineDelimiter(i) != null)) + newText.append(lineDelimiterToUse); + } + catch (org.eclipse.jface.text.BadLocationException exception) { + // should fix up to either throw nothing, or the right thing, + // but + // in the course of refactoring, this was easiest "quick fix". + throw new RuntimeException(exception); + } + } + return newText; + } + + /** + * This method must return a new instance of IEncodedDocument, that has + * been initialized with appropriate parser. For many loaders, the + * (default) parser used is known for any input. For others, the correct + * parser (and its initialization) is normally dependent on the content of + * the file. This no-argument method should assume "empty input" and would + * therefore return the default parser for the default contentType. + */ + public IEncodedDocument createNewStructuredDocument() { + IEncodedDocument structuredDocument = newEncodedDocument(); + // Make sure every structuredDocument has an Encoding Memento, + // which is the default one for "empty" structuredDocuments + String charset = ContentTypeEncodingPreferences.useDefaultNameRules(getDocumentEncodingDetector()); + String specDefaultCharset = getDocumentEncodingDetector().getSpecDefaultEncoding(); + structuredDocument.setEncodingMemento(CodedIO.createEncodingMemento(charset, EncodingMemento.DEFAULTS_ASSUMED_FOR_EMPTY_INPUT, specDefaultCharset)); + + String lineDelimiter = getPreferredNewLineDelimiter(null); + if (lineDelimiter != null) + structuredDocument.setPreferredLineDelimiter(lineDelimiter); + + IDocumentPartitioner defaultPartitioner = getDefaultDocumentPartitioner(); + if (structuredDocument instanceof IDocumentExtension3) { + ((IDocumentExtension3) structuredDocument).setDocumentPartitioner(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, defaultPartitioner); + } + else { + structuredDocument.setDocumentPartitioner(defaultPartitioner); + } + defaultPartitioner.connect(structuredDocument); + + return structuredDocument; + } + + /** + * This abstract version should handle most cases, but won't if + * contentType is sensitive to encoding, and/or embedded types + */ + public IEncodedDocument createNewStructuredDocument(IFile iFile) throws IOException, CoreException { + IEncodedDocument structuredDocument = createNewStructuredDocument(); + + String lineDelimiter = getPreferredNewLineDelimiter(iFile); + if (lineDelimiter != null) + structuredDocument.setPreferredLineDelimiter(lineDelimiter); + + try { + + CodedReaderCreator creator = getCodedReaderCreator(); + creator.set(iFile); + fEncodingMemento = creator.getEncodingMemento(); + structuredDocument.setEncodingMemento(fEncodingMemento); + fFullPreparedReader = getCodedReaderCreator().getCodedReader(); + + setDocumentContentsFromReader(structuredDocument, fFullPreparedReader); + } + finally { + if (fFullPreparedReader != null) { + fFullPreparedReader.close(); + } + } + return structuredDocument; + } + + public IEncodedDocument createNewStructuredDocument(String filename, InputStream inputStream) throws UnsupportedEncodingException, IOException { + return createNewStructuredDocument(filename, inputStream, EncodingRule.CONTENT_BASED); + } + + public IEncodedDocument createNewStructuredDocument(String filename, InputStream inputStream, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException { + if (filename == null && inputStream == null) { + throw new IllegalArgumentException("can not have both null filename and inputstream"); //$NON-NLS-1$ + } + IEncodedDocument structuredDocument = createNewStructuredDocument(); + CodedReaderCreator codedReaderCreator = getCodedReaderCreator(); + try { + codedReaderCreator.set(filename, inputStream); + codedReaderCreator.setEncodingRule(encodingRule); + fEncodingMemento = codedReaderCreator.getEncodingMemento(); + fFullPreparedReader = codedReaderCreator.getCodedReader(); + structuredDocument.setEncodingMemento(fEncodingMemento); + setDocumentContentsFromReader(structuredDocument, fFullPreparedReader); + } + catch (CoreException e) { + // impossible in this context + throw new Error(e); + } + finally { + if (fFullPreparedReader != null) { + fFullPreparedReader.close(); + } + } + + return structuredDocument; + } + + private int getCharPostionOfFailure(BufferedReader inputStream) { + int charPosition = 1; + int charRead = -1; + boolean errorFound = false; + do { + try { + charRead = inputStream.read(); + charPosition++; + } + catch (IOException e) { + // this is expected, since we're expecting failure, + // so no need to do anything. + errorFound = true; + break; + } + } + while (!(charRead == -1 || errorFound)); + + if (errorFound) + // dmw, blindly modified to +1 to get unit tests to work, moving + // from Java 1.3, to 1.4 + // not sure how/why this behavior would have changed. (Its as if + // 'read' is reporting error + // one character early). + return charPosition + 1; + else + return -1; + } + + /** + * @return Returns the codedReaderCreator. + */ + protected CodedReaderCreator getCodedReaderCreator() { + if (fCodedReaderCreator == null) { + fCodedReaderCreator = new CodedReaderCreator(); + } + return fCodedReaderCreator; + } + + /** + * Creates the partitioner to be used with the + * IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING partitioning + * + * @return IDocumentPartitioner + */ + public abstract IDocumentPartitioner getDefaultDocumentPartitioner(); + + /** + * Returns the encodingMemento. + * + * @return EncodingMemento + */ + public EncodingMemento getEncodingMemento() { + if (fEncodingMemento == null) { + throw new IllegalStateException("Program Error: encodingMemento was accessed before it was set"); //$NON-NLS-1$ + } + return fEncodingMemento; + } + + /** + * @return Returns the fullPreparedReader. + */ + protected Reader getFullPreparedReader() throws UnsupportedEncodingException, CoreException, IOException { + if (fFullPreparedReader == null) { + fFullPreparedReader = getCodedReaderCreator().getCodedReader(); + } + return fFullPreparedReader; + } + + /** + * Returns the default line delimiter preference for the given file. + * + * @param file + * the file + * @return the default line delimiter + * @since 3.1 + */ + private String getPlatformLineDelimiterPreference(IFile file) { + IScopeContext[] scopeContext; + if (file != null && file.getProject() != null) { + // project preference + scopeContext = new IScopeContext[]{new ProjectScope(file.getProject())}; + String lineDelimiter = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); + if (lineDelimiter != null) + return lineDelimiter; + } + // workspace preference + scopeContext = new IScopeContext[]{new InstanceScope()}; + return Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); + } + + /** + * @deprecated use getPreferredNewLineDelimiter(IFile) instead + */ + protected String getPreferredNewLineDelimiter() { + return getPreferredNewLineDelimiter(null); + } + + /** + * If subclass doesn't implement, return platform default + */ + protected String getPreferredNewLineDelimiter(IFile file) { + return getPlatformLineDelimiterPreference(file); + } + + /** + * A utility method, but depends on subclasses to impliment the preferred + * end of line for a particular content type. Note: subclasses should not + * re-implement this method (there's no reason to, even though its part of + * interface). This method not only converts end-of-line characters, if + * needed, but sets the correct end-of-line delimiter in + * structuredDocument. Minor note: can't use this exact method in dumpers, + * since the decision to change or not is a little different, and since + * there we have to change text of structuredDocument if found to need + * conversion. (Where as for loading, we assume we haven't yet set text in + * structuredDocument, but will be done by other method just a tiny biy + * later). Needs to be public to handle interface. It is in the interface + * just so ModelManagerImpl can use it in a special circumstance. + */ + public StringBuffer handleLineDelimiter(StringBuffer originalString, IEncodedDocument theFlatModel) { + // TODO: need to handle line delimiters so Marker Positions are + // updated + StringBuffer convertedText = null; + // based on text, make a guess on what's being used as + // line delimiter + String probableLineDelimiter = TextUtilities.determineLineDelimiter(originalString, theFlatModel.getLegalLineDelimiters(), System.getProperty("line.separator")); //$NON-NLS-1$ + String preferredLineDelimiter = getPreferredNewLineDelimiter(null); + if (preferredLineDelimiter == null) { + // when preferredLineDelimiter is null, it means "leave alone" + // so no conversion needed. + // set here, only if null (should already be set, but if not, + // we'll set so any subsequent editing inserts what we're + // assuming) + if (!theFlatModel.getPreferredLineDelimiter().equals(probableLineDelimiter)) { + theFlatModel.setPreferredLineDelimiter(probableLineDelimiter); + } + convertedText = originalString; + } + else { + if (!preferredLineDelimiter.equals(probableLineDelimiter)) { + // technically, wouldn't have to convert line delimiters + // here at beginning, but when we save, if the preferred + // line delimter is "leave alone" then we do leave alone, + // so best to be right from beginning. + convertedText = convertLineDelimiters(originalString, preferredLineDelimiter); + theFlatModel.setPreferredLineDelimiter(preferredLineDelimiter); + } + else { + // they are already the same, no conversion needed + theFlatModel.setPreferredLineDelimiter(preferredLineDelimiter); + convertedText = originalString; + } + } + return convertedText; + } + + protected abstract IEncodedDocument newEncodedDocument(); + + /** + * Very mechanical method, just to read the characters, once the reader is + * correctly created. Can throw MalFormedInputException. + */ + private StringBuffer readInputStream(Reader reader) throws IOException { + + int fBlocksRead = 0; + StringBuffer buffer = new StringBuffer(); + int numRead = 0; + try { + char tBuff[] = new char[CodedIO.MAX_BUF_SIZE]; + while (numRead != -1) { + numRead = reader.read(tBuff, 0, tBuff.length); + if (numRead > 0) { + buffer.append(tBuff, 0, numRead); + fBlocksRead++; + } + } + } + catch (MalformedInputException e) { + throw new MalformedInputExceptionWithDetail(fEncodingMemento.getJavaCharsetName(), fBlocksRead * CodedIO.MAX_BUF_SIZE + numRead + e.getInputLength()); + } + catch (UnmappableCharacterException e) { + throw new MalformedInputExceptionWithDetail(fEncodingMemento.getJavaCharsetName(), fBlocksRead * CodedIO.MAX_BUF_SIZE + numRead + e.getInputLength()); + + } + return buffer; + } + + public void reload(IEncodedDocument encodedDocument, Reader inputStreamReader) throws IOException { + if (inputStreamReader == null) { + throw new IllegalArgumentException("stream reader can not be null"); //$NON-NLS-1$ + } + int READ_BUFFER_SIZE = 8192; + int MAX_BUFFERED_SIZE_FOR_RESET_MARK = 200000; + // temp .... eventually we'lll only read as needed + BufferedReader bufferedReader = new BufferedReader(inputStreamReader, MAX_BUFFERED_SIZE_FOR_RESET_MARK); + bufferedReader.mark(MAX_BUFFERED_SIZE_FOR_RESET_MARK); + StringBuffer buffer = new StringBuffer(); + try { + int numRead = 0; + char tBuff[] = new char[READ_BUFFER_SIZE]; + while ((numRead = bufferedReader.read(tBuff, 0, tBuff.length)) != -1) { + buffer.append(tBuff, 0, numRead); + } + // remember -- we didn't open stream ... so we don't close it + } + catch (MalformedInputException e) { + // int pos = e.getInputLength(); + EncodingMemento localEncodingMemento = getEncodingMemento(); + boolean couldReset = true; + String encodingNameInError = localEncodingMemento.getJavaCharsetName(); + if (encodingNameInError == null) { + encodingNameInError = localEncodingMemento.getDetectedCharsetName(); + } + try { + bufferedReader.reset(); + } + catch (IOException resetException) { + // the only errro that can occur during reset is an + // IOException + // due to already being past the rest mark. In that case, we + // throw more generic message + couldReset = false; + } + // -1 can be used by UI layer as a code that "position could not + // be + // determined" + int charPostion = -1; + if (couldReset) { + + charPostion = getCharPostionOfFailure(bufferedReader); + // getCharPostionOfFailure(new InputStreamReader(inStream, + // javaEncodingNameInError)); + } + // all of that just to throw more accurate error + // note: we do the conversion to ianaName, instead of using the + // local + // variable, + // because this is ultimately only for the user error message + // (that + // is, + // the error occurred + // in context of javaEncodingName no matter what ianaEncodingName + // is + throw new MalformedInputExceptionWithDetail(encodingNameInError, CodedIO.getAppropriateJavaCharset(encodingNameInError), charPostion, !couldReset, MAX_BUFFERED_SIZE_FOR_RESET_MARK); + } + StringBuffer stringbuffer = buffer; + encodedDocument.set(stringbuffer.toString()); + + } + + protected void setDocumentContentsFromReader(IEncodedDocument structuredDocument, Reader reader) throws IOException { + + StringBuffer allText = readInputStream(reader); + structuredDocument.set(allText.toString()); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/DocumentReader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/DocumentReader.java new file mode 100644 index 0000000..ae28004 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/DocumentReader.java
@@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.document; + +import java.io.IOException; +import java.io.Reader; + +import org.eclipse.jface.text.IDocument; + +/** + * A java.io.Reader that can operate off of an IDocument. + */ +public class DocumentReader extends Reader { + private IDocument fDocument = null; + private int mark = 0; + private int position = 0; + + public DocumentReader() { + super(); + } + + public DocumentReader(IDocument document) { + this(document, 0); + } + + public DocumentReader(IDocument document, int initialPosition) { + super(); + fDocument = document; + position = initialPosition; + } + + public void close() throws IOException { + fDocument = null; + } + + /** + * @return + */ + public IDocument getDocument() { + return fDocument; + } + + /* + * (non-Javadoc) + * + * @see java.io.Reader#mark(int) + */ + public void mark(int readAheadLimit) throws IOException { + mark = position; + } + + public boolean markSupported() { + return true; + } + + public int read(char[] cbuf, int off, int len) throws IOException { + if(fDocument == null) + return -1; + + char[] readChars = null; + try { + if (position >= fDocument.getLength()) + return -1; + // the IDocument is likely using a GapTextStore, so we can't + // retrieve a char[] directly + if (position + len > fDocument.getLength()) + readChars = fDocument.get(position, fDocument.getLength() - position).toCharArray(); + else + readChars = fDocument.get(position, len).toCharArray(); + System.arraycopy(readChars, 0, cbuf, off, readChars.length); + // System.out.println("" + position + ":" + readChars.length + " " + // + StringUtils.escape(new String(readChars))); + position += readChars.length; + return readChars.length; + } catch (Exception e) { + throw new IOException("Exception while reading from IDocument: " + e); //$NON-NLS-1$ + } + } + + /* + * (non-Javadoc) + * + * @see java.io.Reader#reset() + */ + public void reset() throws IOException { + position = mark; + } + + public void reset(IDocument document, int initialPosition) { + fDocument = document; + position = initialPosition; + } + + /* + * (non-Javadoc) + * + * @see java.io.Reader#reset() + */ + public void reset(int pos) throws IOException { + position = pos; + } + + /* + * (non-Javadoc) + * + * @see java.io.Reader#skip(long) + */ + public long skip(long n) throws IOException { + if(fDocument == null) + return 0; + + long skipped = n; + if (position + n > fDocument.getLength()) { + skipped = fDocument.getLength() - position; + position = fDocument.getLength(); + } else { + position += n; + } + return skipped; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/IDocumentCharsetDetector.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/IDocumentCharsetDetector.java new file mode 100644 index 0000000..4ea8b04 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/IDocumentCharsetDetector.java
@@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.document; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.encoding.IResourceCharsetDetector; + + + +public interface IDocumentCharsetDetector extends IResourceCharsetDetector { + void set(IDocument document); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/IDocumentLoader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/IDocumentLoader.java new file mode 100644 index 0000000..b7eff1e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/IDocumentLoader.java
@@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.document; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; + + +/** + * Provides methods for the creation of an IStructuredDocument correctly + * prepared to work with a particular type of content. + */ +public interface IDocumentLoader { + + /** + * @return a new IStructuredDocument prepared by this loader + */ + IEncodedDocument createNewStructuredDocument(); + + /** + * This API is like createNewStructuredDocument, except it should populate + * the structuredDocument with the contents of IFile. Also, those + * StructuredDocuments which are sensitive to the input (that is, the + * parser or parser initialization my require the input) should + * additionally initialize the parser, etc., appropriate to the input. + * + * As always, the appropriate decoding should be used. + */ + IEncodedDocument createNewStructuredDocument(IFile iFile) throws java.io.IOException, CoreException; + + /** + * This method must return a new instance of IEncodedDocument, that has + * been initialized with appropriate parser. For many loaders, the + * (default) parser used is known for any input. For others, the correct + * parser (and its initialization) is normally dependent on the content of + * the file. This no-argument method should assume "empty input" and would + * therefore return the default parser for the default contentType. + */ + IEncodedDocument createNewStructuredDocument(String filename, InputStream istream) throws java.io.IOException; + + IEncodedDocument createNewStructuredDocument(String filename, InputStream istream, EncodingRule encodingRule) throws java.io.IOException; + + /** + * @return the document partitioner + */ + IDocumentPartitioner getDefaultDocumentPartitioner(); + + IDocumentCharsetDetector getDocumentEncodingDetector(); + + /** + * A utility method, but depends on subclasses to implement the preferred + * end of line for a particular content type. Note: subclasses should not + * re-implement this method (there's no reason to, even though its part of + * interface). This method not only converts end-of-line characters, if + * needed, but sets the correct end-of-line delimiter in + * structuredDocument. The returned value is either the original string, + * if no conversion is needed, or a new string with end-of-lines + * converted. + * + * @deprecated - the content's line delimiters should be preserved + */ + StringBuffer handleLineDelimiter(StringBuffer originalString, IEncodedDocument theStructuredDocument); + + void reload(IEncodedDocument document, Reader reader) throws IOException; +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/StructuredDocumentFactory.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/StructuredDocumentFactory.java new file mode 100644 index 0000000..41618fa --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/StructuredDocumentFactory.java
@@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.document; + +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocument; +import org.eclipse.wst.sse.core.internal.text.JobSafeStructuredDocument; + + +/** + * At the moment, this is primarily intended as a convenience to help switch + * between various types of threading models in the document, all in a central + * piece of code. + */ +public class StructuredDocumentFactory { + private static final int WRITE_SYNCHRONIZED = 3; + private static final int DEFAULT = WRITE_SYNCHRONIZED; + private static final int UNSYNCHRONIZED = 1; + + private static IStructuredDocument getNewStructuredDocumentInstance(int type, RegionParser parser) { + IStructuredDocument result = null; + switch (type) { + case UNSYNCHRONIZED : + result = new BasicStructuredDocument(parser); + break; + case WRITE_SYNCHRONIZED : + result = new JobSafeStructuredDocument(parser); + break; + + default : + throw new IllegalArgumentException("request document type was not known"); //$NON-NLS-1$ + + } + return result; + } + + /** + * Provides the (system default) structured document initialized with the + * parser. + * + * @param parser + * @return + */ + public static IStructuredDocument getNewStructuredDocumentInstance(RegionParser parser) { + return getNewStructuredDocumentInstance(DEFAULT, parser); + } + + /** + * Not intended to be instantiated + * + */ + private StructuredDocumentFactory() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/TextUtilities.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/TextUtilities.java new file mode 100644 index 0000000..91ec598 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/document/TextUtilities.java
@@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.document; + + +/** + * Collection of text functions. + * + * @deprecated - marked as deprecated to remind us to phase this out (and/or + * move to "finished" version). + */ +public class TextUtilities { + + /** + * @deprecated if possible, its best to use + * IDocument.getLegalLineDelimiters() + */ + public final static String[] fgDelimiters = new String[]{"\n", "\r", "\r\n"};//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + + /** + * Determines which one of fgDelimiters appears first in the text. If none + * of them the hint is returned. + */ + public static String determineLineDelimiter(StringBuffer textBuffer, String[] possibles, String hint) { + try { + // TODO: avoid use of String instance + String text = textBuffer.toString(); + int[] info = indexOf(possibles, text, 0); + return possibles[info[1]]; + } catch (ArrayIndexOutOfBoundsException x) { + } + return hint; + } + + /** + * Returns the position in the string greater than offset of the longest + * matching search string. + */ + private static int[] indexOf(String[] searchStrings, String text, int offset) { + + int[] result = {-1, -1}; + + for (int i = 0; i < searchStrings.length; i++) { + int index = text.indexOf(searchStrings[i], offset); + if (index >= 0) { + + if (result[0] == -1) { + result[0] = index; + result[1] = i; + } else if (index < result[0]) { + result[0] = index; + result[1] = i; + } else if (index == result[0] && searchStrings[i].length() > searchStrings[result[1]].length()) { + result[0] = index; + result[1] = i; + } + } + } + + return result; + + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/filebuffers/BasicStructuredDocumentFactory.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/filebuffers/BasicStructuredDocumentFactory.java new file mode 100644 index 0000000..16467b6 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/filebuffers/BasicStructuredDocumentFactory.java
@@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.filebuffers; + +import org.eclipse.core.filebuffers.IDocumentFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.modelhandler.ModelHandlerRegistry; +import org.eclipse.wst.sse.core.internal.text.JobSafeStructuredDocument; + + +/** + * Generic IDocumentFactory for IStructuredDocuments to be used by the + * org.eclipse.core.filebuffers.documentCreation extension point. This class + * is not meant to be subclassed. + * + * @plannedfor 1.0 + */ +public class BasicStructuredDocumentFactory implements IDocumentFactory, IExecutableExtension { + + /* + * The content type ID used to declare this factory; it is used to find + * the corresponding support for creating the document + */ + private String fContentTypeIdentifier = null; + + /** + * Constructor, only to be used by the + * org.eclipse.core.filebuffers.documentCreation extension point. + */ + public BasicStructuredDocumentFactory() { + super(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.filebuffers.IDocumentFactory#createDocument() + */ + public IDocument createDocument() { + IDocument document = null; + IContentType contentType = Platform.getContentTypeManager().getContentType(getContentTypeIdentifier()); + IModelHandler handler = null; + while (handler == null && !IContentTypeManager.CT_TEXT.equals(contentType.getId())) { + handler = ModelHandlerRegistry.getInstance().getHandlerForContentTypeId(contentType.getId()); + contentType = contentType.getBaseType(); + } + if (handler != null) { + document = handler.getDocumentLoader().createNewStructuredDocument(); + } + else { + document = new JobSafeStructuredDocument(); + } + return document; + } + + private String getContentTypeIdentifier() { + return fContentTypeIdentifier; + } + + /* + * Loads the content type ID to be used when creating the Structured Document. + * + * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, + * java.lang.String, java.lang.Object) + */ + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { + fContentTypeIdentifier = config.getAttribute("contentTypeId"); //$NON-NLS-1$ + if (data != null) { + if (data instanceof String && data.toString().length() > 0) { + fContentTypeIdentifier = (String) data; + } + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/AbstractStructuredFormatProcessor.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/AbstractStructuredFormatProcessor.java new file mode 100644 index 0000000..1e8d690 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/AbstractStructuredFormatProcessor.java
@@ -0,0 +1,524 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Jesper Steen Møller - initial IDocumentExtension4 support - #102822 + * David Carver (Intalio) - bug 300443 - some constants aren't static final + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension4; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.util.Assert; +import org.w3c.dom.Attr; +import org.w3c.dom.Node; + + +public abstract class AbstractStructuredFormatProcessor implements IStructuredFormatProcessor { + protected IStructuredFormatContraints fFormatContraints = null; + protected IProgressMonitor fProgressMonitor = null; + public boolean refreshFormatPreferences = true; // special flag for JUnit + /* + * Max length of text to be formatted to be considered a "small change" + * Used for document rewrite session type. + */ + private static final int MAX_SMALL_FORMAT_SIZE = 1000; + + protected void ensureClosed(OutputStream outputStream, InputStream inputStream) { + + try { + if (inputStream != null) { + inputStream.close(); + } + } + catch (IOException e) { + Logger.logException(e); // hopeless + } + try { + if (outputStream != null) { + outputStream.close(); + } + } + catch (IOException e) { + Logger.logException(e); // hopeless + } + } + + public String formatContent(String input) throws IOException, CoreException { + if (input == null) + return input; + + IStructuredModel structuredModel = null; + InputStream inputStream = null; + try { + // setup structuredModel + // Note: We are getting model for read. Will return formatted + // string and NOT save model. + inputStream = new ByteArrayInputStream(input.getBytes("UTF8")); //$NON-NLS-1$ + String id = inputStream.toString() + "." + getFileExtension(); //$NON-NLS-1$ + structuredModel = StructuredModelManager.getModelManager().getModelForRead(id, inputStream, null); + + // format + formatModel(structuredModel); + + // return output + return structuredModel.getStructuredDocument().get(); + } + finally { + ensureClosed(null, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + + public String formatContent(String input, int start, int length) throws IOException, CoreException { + if (input == null) + return input; + + if ((start >= 0) && (length >= 0) && (start + length <= input.length())) { + IStructuredModel structuredModel = null; + InputStream inputStream = null; + try { + // setup structuredModel + // Note: We are getting model for read. Will return formatted + // string and NOT save model. + inputStream = new ByteArrayInputStream(input.getBytes("UTF8")); //$NON-NLS-1$ + String id = inputStream.toString() + "." + getFileExtension(); //$NON-NLS-1$ + structuredModel = StructuredModelManager.getModelManager().getModelForRead(id, inputStream, null); + + // format + formatModel(structuredModel, start, length); + + // return output + return structuredModel.getStructuredDocument().get(); + } + finally { + ensureClosed(null, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromRead(); + } + } + else + return input; + } + + public void formatDocument(IDocument document) throws IOException, CoreException { + if (document == null) + return; + + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if model + // changed. + structuredModel = StructuredModelManager.getModelManager().getExistingModelForEdit(document); + + // format + formatModel(structuredModel); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } + finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + + public void formatDocument(IDocument document, int start, int length) throws IOException, CoreException { + if (document == null) + return; + + if ((start >= 0) && (length >= 0) && (start + length <= document.getLength())) { + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if + // model changed. + structuredModel = StructuredModelManager.getModelManager().getExistingModelForEdit(document); + + if (structuredModel != null) { + // format + formatModel(structuredModel, start, length); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } + } + finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + } + + public void formatFile(IFile file) throws IOException, CoreException { + if (file == null) + return; + + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if model + // changed. + structuredModel = StructuredModelManager.getModelManager().getModelForEdit(file); + + // format + formatModel(structuredModel); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } + finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) { + structuredModel.releaseFromEdit(); + } + + } + } + + public void formatFile(IFile file, int start, int length) throws IOException, CoreException { + if (file == null) + return; + + IStructuredModel structuredModel = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if model + // changed. + structuredModel = StructuredModelManager.getModelManager().getModelForEdit(file); + + // format + formatModel(structuredModel, start, length); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } + finally { + // ensureClosed(outputStream, null); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + + public void formatFileName(String fileName) throws IOException, CoreException { + if (fileName == null) + return; + + IStructuredModel structuredModel = null; + InputStream inputStream = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if model + // changed. + inputStream = new FileInputStream(fileName); + structuredModel = StructuredModelManager.getModelManager().getModelForEdit(fileName, inputStream, null); + + // format + formatModel(structuredModel); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } + finally { + // ensureClosed(outputStream, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + + public void formatFileName(String fileName, int start, int length) throws IOException, CoreException { + if (fileName == null) + return; + + IStructuredModel structuredModel = null; + InputStream inputStream = null; + // OutputStream outputStream = null; + try { + // setup structuredModel + // Note: We are getting model for edit. Will save model if model + // changed. + inputStream = new FileInputStream(fileName); + structuredModel = StructuredModelManager.getModelManager().getModelForEdit(fileName, inputStream, null); + + // format + formatModel(structuredModel, start, length); + + // save model if needed + if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded()) + structuredModel.save(); + } + finally { + // ensureClosed(outputStream, inputStream); + // release from model manager + if (structuredModel != null) + structuredModel.releaseFromEdit(); + } + } + + public void formatModel(IStructuredModel structuredModel) { + int start = 0; + int length = structuredModel.getStructuredDocument().getLength(); + + formatModel(structuredModel, start, length); + } + + public void formatModel(IStructuredModel structuredModel, int start, int length) { + if (structuredModel != null) { + // for debugging purposes + long startTime = System.currentTimeMillis(); + + IDocumentExtension4 docExt4 = null; + if (structuredModel.getStructuredDocument() instanceof IDocumentExtension4) { + docExt4 = (IDocumentExtension4) structuredModel.getStructuredDocument(); + } + DocumentRewriteSession rewriteSession = null; + + try { + // whenever formatting model, fire abouttochange/modelchanged + structuredModel.aboutToChangeModel(); + DocumentRewriteSessionType rewriteType = (length > MAX_SMALL_FORMAT_SIZE) ? DocumentRewriteSessionType.UNRESTRICTED : DocumentRewriteSessionType.UNRESTRICTED_SMALL; + rewriteSession = (docExt4 == null || docExt4.getActiveRewriteSession() != null) ? null : docExt4.startRewriteSession(rewriteType); + + if ((start == 0) && (length == structuredModel.getStructuredDocument().getLength())) + setFormatWithSiblingIndent(structuredModel, false); + else + setFormatWithSiblingIndent(structuredModel, true); + + if ((start >= 0) && (length >= 0) && (start + length <= structuredModel.getStructuredDocument().getLength())) { + List activeNodes = getAllActiveNodes(structuredModel, start, length); + if (activeNodes.size() > 0) { + Node firstNode = (Node) activeNodes.get(0); + Node lastNode = (Node) activeNodes.get(activeNodes.size() - 1); + + boolean done = false; + Node eachNode = firstNode; + Node nextNode = null; + while (!done) { + // update "done" + done = (eachNode == lastNode); + + /* + * get next sibling before format because eachNode + * may be deleted, for example when it's an empty + * text node + */ + nextNode = eachNode.getNextSibling(); + + // format each node + formatNode(eachNode); + + // update each node + if ((nextNode != null) && (nextNode.getParentNode() == null)) + // nextNode is deleted during format + eachNode = eachNode.getNextSibling(); + else + eachNode = nextNode; + + // This should not be needed, but just in case + // something went wrong with with eachNode. + // We don't want an infinite loop here. + if (eachNode == null) + done = true; + } + + } + } + } + finally { + // we need two finally's, just in case first fails + try { + if ((docExt4 != null) && (rewriteSession != null)) + docExt4.stopRewriteSession(rewriteSession); + } + finally { + // always make sure to fire changedmodel when done + structuredModel.changedModel(); + } + } + + if (Logger.DEBUG_FORMAT) { + long endTime = System.currentTimeMillis(); + System.out.println("formatModel time: " + (endTime - startTime)); //$NON-NLS-1$ + } + } + } + + public void formatNode(Node node) { + if (node != null) { + Node newNode = node; + + // format the owner node if it's an attribute node + if (node.getNodeType() == Node.ATTRIBUTE_NODE) + newNode = ((Attr) node).getOwnerElement(); + + // refresh format preferences before getting formatter + if (refreshFormatPreferences) + refreshFormatPreferences(); + + // get formatter and format contraints + IStructuredFormatter formatter = getFormatter(newNode); + // TODO_future: added assert to replace "redundant null check". + // if formatter is ever null, we should provide some + // default formatter to serve as place holder. + Assert.isNotNull(formatter, "formatter was null for a node, "); //$NON-NLS-1$ + IStructuredFormatContraints formatContraints = formatter.getFormatContraints(); + formatContraints.setFormatWithSiblingIndent(true); + // format each node + formatter.format(newNode, formatContraints); + } + } + + /** + * @deprecated Use getAllActiveNodes instead + */ + protected Vector getActiveNodes(IStructuredModel structuredModel, int startNodeOffset, int length) { + List allActiveNodes = getAllActiveNodes(structuredModel, startNodeOffset, length); + return new Vector(allActiveNodes); + } + + protected List getAllActiveNodes(IStructuredModel structuredModel, int startNodeOffset, int length) { + List activeNodes = new ArrayList(); + + if (structuredModel != null) { + Node startNode = (Node) structuredModel.getIndexedRegion(startNodeOffset); + // see https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4711 + // + // We have to watch for selection boundary conditions. Use this as + // an example: <a>text</a><b>text</b>, + // If the whole <a> node is selected, like: + // |<a>text</a>|<b>text</b>, we need to substract the length by 1 + // to find + // the node at the end of the selection: + // structuredModel.getIndexedRegion(startNodeOffset + length - 1), + // or else + // we'd find the next adjacent node. + // + // However, when the selection length is 0 (meaning no text is + // selected), the cursor is at the beginning + // of the node we want to format: |<a>text</a><b>text</b>, the + // node at the end of the selection is: + // structuredModel.getIndexedRegion(startNodeOffset + length). + int endNodeOffset = length > 0 ? startNodeOffset + length - 1 : startNodeOffset + length; + Node endNode = (Node) structuredModel.getIndexedRegion(endNodeOffset); + + // make sure it's an non-empty document + if (startNode != null) { + while (isSiblingOf(startNode, endNode) == false) { + if (endNode != null) + endNode = endNode.getParentNode(); + if (endNode == null) { + startNode = startNode.getParentNode(); + // see + // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4711 + // and notes above + endNodeOffset = length > 0 ? startNodeOffset + length - 1 : startNodeOffset + length; + endNode = (Node) structuredModel.getIndexedRegion(endNodeOffset); + } + } + + while (startNode != endNode) { + activeNodes.add(startNode); + startNode = startNode.getNextSibling(); + } + if (startNode != null) + activeNodes.add(startNode); + } + } + + return activeNodes; + } + + abstract protected String getFileExtension(); + + protected IStructuredFormatContraints getFormatContraints(IStructuredModel structuredModel) { + // 262135 - NPE during format of empty document + if ((fFormatContraints == null) && (structuredModel != null)) { + Node node = (Node) structuredModel.getIndexedRegion(0); + + if (node != null) { + IStructuredFormatter formatter = getFormatter(node); + if (formatter != null) { + fFormatContraints = formatter.getFormatContraints(); + } + } + } + + return fFormatContraints; + } + + abstract protected IStructuredFormatter getFormatter(Node node); + + protected boolean isSiblingOf(Node node, Node endNode) { + if (endNode == null) + return true; + else { + Node siblingNode = node; + while (siblingNode != null) { + if (siblingNode == endNode) + return true; + else + siblingNode = siblingNode.getNextSibling(); + } + return false; + } + } + + abstract protected void refreshFormatPreferences(); + + protected void setFormatWithSiblingIndent(IStructuredModel structuredModel, boolean formatWithSiblingIndent) { + // 262135 - NPE during format of empty document + IStructuredFormatContraints formatContraints = getFormatContraints(structuredModel); + + if (formatContraints != null) + formatContraints.setFormatWithSiblingIndent(formatWithSiblingIndent); + } + + public void setProgressMonitor(IProgressMonitor monitor) { + fProgressMonitor = monitor; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatContraints.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatContraints.java new file mode 100644 index 0000000..2730043 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatContraints.java
@@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +/** + * These are items that change from element to element. + * Passed from node to node in a recursive call. + * eg. current indent is 2 deep, but for the next node might be 3... + */ +public interface IStructuredFormatContraints { + boolean getClearAllBlankLines(); + + String getCurrentIndent(); + + boolean getFormatWithSiblingIndent(); + + boolean getInPreserveSpaceElement(); + + /** + * some special elements can ignore clearing blank lines + * */ + void setClearAllBlankLines(boolean clearAllBlankLines); + + void setCurrentIndent(String currentIndent); + + void setFormatWithSiblingIndent(boolean formatWithSiblingIndent); + + void setInPreserveSpaceElement(boolean inPreserveSpaceElement); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatPreferences.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatPreferences.java new file mode 100644 index 0000000..2cef142 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatPreferences.java
@@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +/** + * These are items that do not change from element to element. + * Passed from node to node in a recursive call because sometimes + * child nodes don't have access to the preferences + */ +public interface IStructuredFormatPreferences { + + boolean getClearAllBlankLines(); + + String getIndent(); + + int getLineWidth(); + + void setClearAllBlankLines(boolean clearAllBlankLines); + + void setIndent(String indent); + + void setLineWidth(int lineWidth); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatProcessor.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatProcessor.java new file mode 100644 index 0000000..b83aac3 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatProcessor.java
@@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +import java.io.IOException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.w3c.dom.Node; + +/** + * The main formatting engine. + * Loops through all the nodes in an IStructuredModel. + */ +public interface IStructuredFormatProcessor { + + /** + * This form of the FormatProcessor takes an IDocument as input, creates a + * temporary model of content type calculated using the IDocument's file + * extension, formats the model within start and length, then releases the + * model. + */ + void formatDocument(IDocument document, int start, int length) throws IOException, CoreException; + + /** + * This form of the FormatProcessor takes an IFile as input, creates a + * temporary model of content type calculated using the IFile's file + * extension, formats the whole model, then releases the model. + */ + void formatFile(IFile file) throws IOException, CoreException; + + /** + * This form of the FormatProcessor takes a model as input, and formats + * the whole model. + */ + void formatModel(IStructuredModel structuredModel); + + /** + * This form of the FormatProcessor takes a model as input, and formats + * the model within start and length. + */ + void formatModel(IStructuredModel structuredModel, int start, int length); + + /** + * This form of the FormatProcessor takes a node as input, and formats the + * node and all its children. + */ + void formatNode(Node node); + + /** + * Sets the progress monitor for this <code>IStructuredFormatProcessor</code>. + * The monitor is used to display progress or cancel if the formatter is run + * in a background job. + * @param monitor + */ + void setProgressMonitor(IProgressMonitor monitor); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatter.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatter.java new file mode 100644 index 0000000..6c23d12 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/IStructuredFormatter.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.w3c.dom.Node; + +/** + * Knows how to format a particular node. + * + * eg. generic node, text node, document node, comment, etc... + */ +public interface IStructuredFormatter { + + void format(Node node); + + void format(Node node, IStructuredFormatContraints formatContraints); + + IStructuredFormatContraints getFormatContraints(); + + IStructuredFormatPreferences getFormatPreferences(); + + void setFormatPreferences(IStructuredFormatPreferences formatPreferences); + + void setProgressMonitor(IProgressMonitor monitor); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/StructuredFormatContraints.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/StructuredFormatContraints.java new file mode 100644 index 0000000..ca47e41 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/StructuredFormatContraints.java
@@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Jesper Steen Møller - xml:space='preserve' support + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +public class StructuredFormatContraints implements IStructuredFormatContraints { + private boolean fClearAllBlankLines; + private String fCurrentIndent = ""; //$NON-NLS-1$ + private boolean fFormatWithSiblingIndent = false; + private boolean fInPreserveSpaceElement = false; + + public boolean getClearAllBlankLines() { + return fClearAllBlankLines; + } + + public String getCurrentIndent() { + return fCurrentIndent; + } + + public boolean getFormatWithSiblingIndent() { + return fFormatWithSiblingIndent; + } + + public void setClearAllBlankLines(boolean clearAllBlankLines) { + fClearAllBlankLines = clearAllBlankLines; + } + + public void setCurrentIndent(String currentIndent) { + fCurrentIndent = currentIndent; + } + + public void setFormatWithSiblingIndent(boolean formatWithSiblingIndent) { + fFormatWithSiblingIndent = formatWithSiblingIndent; + } + + public boolean getInPreserveSpaceElement() { + return fInPreserveSpaceElement; + } + + public void setInPreserveSpaceElement(boolean inPreserveSpaceElement) { + fInPreserveSpaceElement = inPreserveSpaceElement; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/StructuredFormatPreferences.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/StructuredFormatPreferences.java new file mode 100644 index 0000000..96f1b92 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/format/StructuredFormatPreferences.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.format; + +public class StructuredFormatPreferences implements IStructuredFormatPreferences { + private boolean fClearAllBlankLines; + private String fIndent; + private int fLineWidth; + + public boolean getClearAllBlankLines() { + return fClearAllBlankLines; + } + + public String getIndent() { + return fIndent; + } + + public int getLineWidth() { + return fLineWidth; + } + + public void setClearAllBlankLines(boolean clearAllBlankLines) { + fClearAllBlankLines = clearAllBlankLines; + } + + public void setIndent(String indent) { + fIndent = indent; + } + + public void setLineWidth(int lineWidth) { + fLineWidth = lineWidth; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/AbstractModelHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/AbstractModelHandler.java new file mode 100644 index 0000000..6c45577 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/AbstractModelHandler.java
@@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.modelhandler; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.wst.sse.core.internal.document.IDocumentCharsetDetector; +import org.eclipse.wst.sse.core.internal.modelhandler.PluginContributedFactoryReader; + +/** + * ISSUE: need to provide this functionality in improved API. + */ + +public abstract class AbstractModelHandler implements IModelHandler { + private String associatedContentTypeId; + private boolean defaultSetting; + private String modelHandlerID; + + public AbstractModelHandler() { + super(); + } + + /** + * These factories are added automatically by model manager + */ + public List getAdapterFactories() { + List result = new ArrayList(); + Collection holdFactories = PluginContributedFactoryReader.getInstance().getFactories(this); + if (holdFactories != null) { + result.addAll(holdFactories); + } + return result; + } + + public String getAssociatedContentTypeId() { + return associatedContentTypeId; + } + + public abstract IDocumentCharsetDetector getEncodingDetector(); + + public String getId() { + return modelHandlerID; + } + + public boolean isDefault() { + return defaultSetting; + } + + protected void setAssociatedContentTypeId(String contentTypeId) { + associatedContentTypeId = contentTypeId; + } + + public void setDefault(boolean defaultParam) { + defaultSetting = defaultParam; + } + + protected void setId(String id) { + modelHandlerID = id; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/EmbeddedTypeHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/EmbeddedTypeHandler.java new file mode 100644 index 0000000..b551190 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/EmbeddedTypeHandler.java
@@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.modelhandler; + +import java.util.List; + +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; + + +/** + */ +public interface EmbeddedTypeHandler { + + /** + * These AdapterFactories are NOT added to IStructuredModel's + * IAdapterFactory Registry, but instead expected to be consulted as + * needed by functionality aware of embedded content types. Additions + * to the model's own factory registry should be done in + * {@link #initializeFactoryRegistry(FactoryRegistry)} + */ + List getAdapterFactories(); + + /** + * Returns the unique identifier for the content type family this + * ContentTypeDescription belongs to. + */ + String getFamilyId(); + + /** + * Returns a list of mime types (as Strings) this handler is appropriate + * for + */ + List getSupportedMimeTypes(); + + /** + * If this hander can handle a given mimeType. + * + * This is a looser check than simply checking if a give mimeType + * in the list of supported types, so it should be used with that + * in mind. That is, the supported mimeType list should ideally be + * checked first. + * + * eg. if a mime type ends with "+xml", like voice+xml + * the EmbeddedXML handler should be able to handle it + * + * @return true if this handler thinks can handle the given mimeType + */ + boolean canHandleMimeType(String mimeType); + + /** + * This method is to give the EmbeddedContentType an opportunity to add + * factories directly to the IStructuredModel's IAdapterFactory registry. + */ + void initializeFactoryRegistry(FactoryRegistry registry); + + /** + * initializeParser, for example, setting up a "block" tags list using an + * extended interface + */ + void initializeParser(RegionParser parser); + + boolean isDefault(); + + EmbeddedTypeHandler newInstance(); + + void uninitializeFactoryRegistry(FactoryRegistry registry); + + void uninitializeParser(RegionParser parser); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/IDocumentTypeHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/IDocumentTypeHandler.java new file mode 100644 index 0000000..aa62963 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/IDocumentTypeHandler.java
@@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.modelhandler; + +import org.eclipse.wst.sse.core.internal.document.IDocumentCharsetDetector; +import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; + +/** + * Responsible for providing the mechanisms used in the correct loading of an + * IStructuredDocument's contents and determine its self-described encoding. + */ +public interface IDocumentTypeHandler { + + /** + * The Loader is reponsible for decoding the Resource, + */ + IDocumentLoader getDocumentLoader(); + + /** + * @deprecated - likely to go away, so I marked as deprecated to + * discoursage use + */ + IDocumentCharsetDetector getEncodingDetector(); + + /** + * Must return unique ID that is the same as identified in plugin registry + */ + String getId(); + + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/IModelHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/IModelHandler.java new file mode 100644 index 0000000..588f39e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/modelhandler/IModelHandler.java
@@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.modelhandler; + +import java.util.List; + +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; + + +/** + * Responsible for providing the mechanisms used in the correct loading of an + * IStructuredModel's contents and initialization of its adapter factories. + */ +public interface IModelHandler extends IDocumentTypeHandler { + /** + * This method should return Factories which are added automatically by + * IModelManager. This can and will often be an empty List (or null), + * since some AdapterFactories must be added by Loader directly, and most + * should be added by Editors. FormatAdapterFactory is an example of one + * that can be returned here, since the timing of adding it is not + * critical, but it may be needed even when an editor is not being used. + */ + List getAdapterFactories(); + + /** + * Returns the ID for the associated ContentTypeHandler But is needed for + * now. + */ + String getAssociatedContentTypeId(); + + /** + * The Loader is reponsible for decoding the Resource, + */ + IModelLoader getModelLoader(); + + boolean isDefault(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockMarker.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockMarker.java new file mode 100644 index 0000000..468b360 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockMarker.java
@@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + + + +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + + +/** + * ISSUE: need to provide functionality in improved API. + */ +public class BlockMarker extends TagMarker { + + // allow for JSP expressions within the block + protected boolean fAllowJSP = true; + + protected boolean fCaseSensitive = false; + + // the context for the contents of this tag (BLOCK_TEXT, JSP_CONTENT, + // etc.) + protected String fContext; + + public BlockMarker(String tagName, ITextRegion marker, String context) { + this(tagName, marker, context, true); + } + + public BlockMarker(String tagName, ITextRegion marker, String context, boolean caseSensitive) { + this(tagName, marker, context, caseSensitive, true); + } + + public BlockMarker(String tagName, ITextRegion marker, String context, boolean caseSensitive, boolean allowJSP) { + super(tagName, marker); + setContext(context); + setCaseSensitive(caseSensitive); + setAllowJSP(allowJSP); + } + + public BlockMarker(String tagName, String regionContext, boolean caseSensitive) { + this(tagName, null, regionContext, caseSensitive, false); + } + + /** + * Gets the allowJSP. + * + * @return Returns a boolean + */ + public boolean allowsJSP() { + return fAllowJSP; + } + + /** + * Gets the context. + * + * @return Returns a String + */ + public String getContext() { + return fContext; + } + + /** + * + * @return boolean + */ + public final boolean isCaseSensitive() { + return fCaseSensitive; + } + + /** + * Sets the allowJSP. + * + * @param allowJSP + * The allowJSP to set + */ + public void setAllowJSP(boolean allowJSP) { + fAllowJSP = allowJSP; + } + + public final void setCaseSensitive(boolean sensitive) { + fCaseSensitive = sensitive; + } + + /** + * Sets the context. + * + * @param context + * The context to set + */ + public void setContext(String context) { + fContext = context; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockTagParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockTagParser.java new file mode 100644 index 0000000..6bd6b70 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockTagParser.java
@@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + + + +import java.util.List; + +public interface BlockTagParser { + + void addBlockMarker(BlockMarker marker); + + void beginBlockScan(String tagName); + + BlockMarker getBlockMarker(String tagName); + + List getBlockMarkers(); + + void removeBlockMarker(BlockMarker marker); + + void removeBlockMarker(String tagName); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockTokenizer.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockTokenizer.java new file mode 100644 index 0000000..b5ad534 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/BlockTokenizer.java
@@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + + + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.List; + +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + + +public interface BlockTokenizer { + + void addBlockMarker(BlockMarker marker); + + void beginBlockMarkerScan(String newTagName, String context); + + void beginBlockTagScan(String newTagName); + + List getBlockMarkers(); + + ITextRegion getNextToken() throws IOException; + + int getOffset(); + + boolean isEOF(); + + BlockTokenizer newInstance(); + + void removeBlockMarker(BlockMarker marker); + + void removeBlockMarker(String tagname); + + void reset(char[] charArray); + + void reset(char[] charArray, int newOffset); + + void reset(InputStream in); + + void reset(InputStream in, int newOffset); + + void reset(Reader in); + + void reset(Reader in, int newOffset); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/IBlockedStructuredDocumentRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/IBlockedStructuredDocumentRegion.java new file mode 100644 index 0000000..30eab0c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/IBlockedStructuredDocumentRegion.java
@@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; + +/** + * IBlockedStructuredDocumentRegion is just like an IStructuredDocumentRegion + * except results from parsing a "block tag" (such as SCRIPT or STYLE). + * Because these are "variable" partition types, its often handy (efficient) + * to keep track of the partition type. + * + * @plannedfor 1.0 + */ +public interface IBlockedStructuredDocumentRegion extends IStructuredDocumentRegion { + /** + * Return the partion type for this region. + * + * @return the partion type. + */ + String getPartitionType(); + + /** + * Sets the partion type. + * + * For use by parsers and re-parsers only. + * + * @param partitionType + */ + void setPartitionType(String partitionType); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/JSPCapableParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/JSPCapableParser.java new file mode 100644 index 0000000..3d55973 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/JSPCapableParser.java
@@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + +import java.util.List; + +public interface JSPCapableParser extends RegionParser, BlockTagParser { + void addNestablePrefix(TagMarker marker); + + /** + * returns the TagMarkers for prefixes that are allowed to be nestable + * + * @return + */ + List getNestablePrefixes(); + + void removeNestablePrefix(String tagName); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/RegionParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/RegionParser.java new file mode 100644 index 0000000..628dfc6 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/RegionParser.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + + + +import java.io.Reader; +import java.util.List; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; + + +public interface RegionParser { + + IStructuredDocumentRegion getDocumentRegions(); + + List getRegions(); + + /** + * The 'newInstance' method is similar to 'clone', but does not include + * the copying of any content. For a pure RegionParser itself, there would + * be little state to "clone", but for some subtypes, such as + * StructuredDocumentRegionParser and JSPCapableParser, there could the + * more internal data to "clone", such as the internal tokenizer should be + * cloned (including block tags, etc). + */ + RegionParser newInstance(); + + void reset(Reader reader); + + /** + * An additional offset for use with any position-dependant parsing rules + */ + void reset(Reader reader, int offset); + + void reset(String input); + + /** + * An additional offset for use with any position-dependant parsing rules + */ + void reset(String input, int offset); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionHandler.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionHandler.java new file mode 100644 index 0000000..31e5cba --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionHandler.java
@@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + + + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; + +public interface StructuredDocumentRegionHandler { + + // Sent when a IStructuredDocumentRegion is first parsed + public void nodeParsed(IStructuredDocumentRegion aCoreStructuredDocumentRegion); + + // Sent when the calling parser's model undergoes a full reset + // and any information based upon the old model should be + // cleared + public void resetNodes(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionHandlerExtension.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionHandlerExtension.java new file mode 100644 index 0000000..85f2d7b --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionHandlerExtension.java
@@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + + +public interface StructuredDocumentRegionHandlerExtension extends StructuredDocumentRegionHandler { + void setStructuredDocument(IStructuredDocument newDocument); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionParser.java new file mode 100644 index 0000000..d4bbe5d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionParser.java
@@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + + + +public interface StructuredDocumentRegionParser extends RegionParser { + + void addStructuredDocumentRegionHandler(StructuredDocumentRegionHandler handler); + + void removeStructuredDocumentRegionHandler(StructuredDocumentRegionHandler handler); + + void resetHandlers(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionParserExtension.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionParserExtension.java new file mode 100644 index 0000000..474950e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/StructuredDocumentRegionParserExtension.java
@@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + +import java.util.List; + + + +public interface StructuredDocumentRegionParserExtension extends StructuredDocumentRegionParser { + /** + * Returns the current list of StructuredDocumentRegionHandlers listening + * to this parser. + * + * @return List - the list of listeners, the list may not be null and each + * element in it must implement StructuredDocumentRegionHandler + */ + List getStructuredDocumentRegionHandlers(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/TagMarker.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/TagMarker.java new file mode 100644 index 0000000..3de225d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/ltk/parser/TagMarker.java
@@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.ltk.parser; + +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + +/** + * ISSUE: need to provide functionality in improved API. + */ + +public class TagMarker { + + // a ITextRegion (meant to be updated with the model) marking the position + // where this tagname becomes effective + protected ITextRegion fMarker = null; + + // the tagname + protected String fTagName = null; + + /** + * + */ + public TagMarker() { + super(); + } + + public TagMarker(String tagname) { + super(); + setTagName(tagname); + } + + public TagMarker(String tagname, ITextRegion marker) { + super(); + setTagName(tagname); + setMarker(marker); + } + + public final ITextRegion getMarker() { + return fMarker; + } + + /** + * @return java.lang.String + */ + public final String getTagName() { + return fTagName; + } + + /** + * @return boolean + */ + public boolean isGlobal() { + return fMarker == null; + } + + /** + * @param newMarker + */ + public final void setMarker(ITextRegion newMarker) { + fMarker = newMarker; + } + + /** + * @param newTagname + * java.lang.String + */ + public final void setTagName(String newTagName) { + fTagName = newTagName; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractModelLoader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractModelLoader.java new file mode 100644 index 0000000..caafaaf --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractModelLoader.java
@@ -0,0 +1,545 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.model; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocumentExtension3; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTagParser; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionHandler; +import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionHandlerExtension; +import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionParser; +import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionParserExtension; +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredPartitioning; +import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocument; +import org.eclipse.wst.sse.core.internal.text.rules.StructuredTextPartitioner; +import org.eclipse.wst.sse.core.internal.util.Assert; + + +/** + * This class reads a file and creates an Structured Model. + */ +public abstract class AbstractModelLoader implements IModelLoader { + protected static final int encodingNameSearchLimit = 1000; + + private static long computeMem() { + for (int i = 0; i < 5; i++) { + System.gc(); + System.runFinalization(); + } + return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + + private boolean DEBUG = false; + protected IDocumentLoader documentLoaderInstance; + + /** + * AbstractLoader constructor also initializes encoding converter/mapper + */ + public AbstractModelLoader() { + super(); + } + + protected void addFactories(IStructuredModel model, List factoryList) { + Assert.isNotNull(model); + FactoryRegistry registry = model.getFactoryRegistry(); + Assert.isNotNull(registry, "IStructuredModel " + model.getId() + " has a null FactoryRegistry"); //$NON-NLS-1$ //$NON-NLS-2$ + if (factoryList != null) { + Iterator iterator = factoryList.iterator(); + while (iterator.hasNext()) { + INodeAdapterFactory factory = (INodeAdapterFactory) iterator.next(); + registry.addFactory(factory); + } + } + } + + /** + * This method should perform all the model initialization required before + * it contains content, namely, it should call newModel, the + * createNewStructuredDocument(), then add those adapter factories which + * must be set before content is applied. This method should be called by + * "load" method. (this is tentative API) + */ + public IStructuredModel createModel() { + documentLoaderInstance = null; + IStructuredModel model = newModel(); + IEncodedDocument structuredDocument = getDocumentLoader().createNewStructuredDocument(); + if (structuredDocument instanceof IStructuredDocument) { + model.setStructuredDocument((IStructuredDocument) structuredDocument); + addFactories(model, getAdapterFactories()); + // + initEmbeddedTypePre(model, (IStructuredDocument) structuredDocument); + initEmbeddedTypePost(model); + // For types with propagating adapters, its important + // that the propagating adapter be in place before the contents + // are set. + preLoadAdapt(model); + } + return model; + } + + public IStructuredModel createModel(IStructuredDocument structuredDocument, String baseLocation, IModelHandler handler) { + documentLoaderInstance = null; + IStructuredModel model = newModel(); + model.setBaseLocation(baseLocation); + + // handler must be set early, incase a re-init is + // required during creation. + model.setModelHandler(handler); + + addFactories(model, getAdapterFactories()); + // For types with propagating adapters, it's important + // that the propagating adapter be in place before the contents + // are set. + preLoadAdapt(model); + initEmbeddedTypePre(model, structuredDocument); + + model.setStructuredDocument(structuredDocument); + // + initEmbeddedTypePost(model); + + return model; + } + + /** + * This method is used for cloning models. + */ + public IStructuredModel createModel(IStructuredModel oldModel) { + documentLoaderInstance = null; + IStructuredModel newModel = newModel(); + IStructuredDocument oldStructuredDocument = oldModel.getStructuredDocument(); + IStructuredDocument newStructuredDocument = oldStructuredDocument.newInstance(); + newModel.setStructuredDocument(newStructuredDocument); + // NOTE: we DO NOT simply add the standard ones to the new model + // addFactories(newModel, getAdapterFactories()); + // Now, we take the opportunity to add Factories from the oldModel's + // registry to the new model's registry .. if they do not already + // exist there. + duplicateFactoryRegistry(newModel, oldModel); + if (newModel instanceof AbstractStructuredModel) { + ((AbstractStructuredModel) newModel).setContentTypeIdentifier(oldModel.getContentTypeIdentifier()); + } + // addFactories(newModel, oldModel); + initEmbeddedType(oldModel, newModel); + // For types with propagating adapters, its important + // that the propagating adapter be in place before the contents + // are set. + preLoadAdapt(newModel); + return newModel; + } + + private void duplicateFactoryRegistry(IStructuredModel newModel, IStructuredModel oldModel) { + List oldAdapterFactories = oldModel.getFactoryRegistry().getFactories(); + List newAdapterFactories = new ArrayList(); + Iterator oldListIterator = oldAdapterFactories.iterator(); + while (oldListIterator.hasNext()) { + INodeAdapterFactory oldAdapterFactory = (INodeAdapterFactory) oldListIterator.next(); + // now "clone" the adapterfactory + newAdapterFactories.add(oldAdapterFactory.copy()); + } + // now that we have the "cloned" list, add to new model + addFactories(newModel, newAdapterFactories); + } + + /** + * This method must return those factories which must be attached to the + * structuredModel before content is applied. + */ + public List getAdapterFactories() { + // abstract method returns none + return new ArrayList(0); + } + + abstract public IDocumentLoader getDocumentLoader(); + + /** + * Method initEmbeddedType, "pre"-stage. Nothing to do here in super class. + * + * @param model + */ + protected void initEmbeddedTypePre(IStructuredModel model) { + } + + /** + * Method initEmbeddedType, "pre"-stage. By default simply calls the + * version of this method that uses only the structured model. + * + * @param model + * the model for which to initialize + * @param structuredDocument + * The structured document containing the text content for the + * model, which may be a different instance than what is in the + * model at this stage. + */ + protected void initEmbeddedTypePre(IStructuredModel model, IStructuredDocument structuredDocument) { + initEmbeddedTypePre(model); + } + + protected void initEmbeddedTypePost(IStructuredModel model) { + } + + /** + * Method initEmbeddedType. Nothing to do here in super class. + * + * @param oldModel + * @param newModel + */ + protected void initEmbeddedType(IStructuredModel oldModel, IStructuredModel newModel) { + } + + public void load(IFile file, IStructuredModel model) throws IOException, CoreException { + IEncodedDocument structuredDocument = model.getStructuredDocument(); + if (file == null) + structuredDocument = getDocumentLoader().createNewStructuredDocument(); + else + structuredDocument = getDocumentLoader().createNewStructuredDocument(file); + + // TODO: need to straighten out IEncodedDocument mess + if (structuredDocument instanceof IStructuredDocument) + transformInstance(model.getStructuredDocument(), (IStructuredDocument) structuredDocument); + else + model.getStructuredDocument().set(structuredDocument.get()); + + // original hack + // model.setStructuredDocument((IStructuredDocument) + // structuredDocument); + // ((IStructuredDocument) structuredDocument).fireNewDocument(this); + documentLoaderInstance = null; + // technicq of future + // model.setStructuredDocument((IStructuredDocument) + // structuredDocument); + // documentLoaderInstance = null; + } + + public void load(InputStream inputStream, IStructuredModel model, EncodingRule encodingRule) throws UnsupportedEncodingException, java.io.IOException { + // note we don't open the stream, so we don't close it + IEncodedDocument structuredDocument = model.getStructuredDocument(); + if (inputStream == null) { + structuredDocument = getDocumentLoader().createNewStructuredDocument(); + } + else { + // assume's model has been initialized already with base location + structuredDocument = getDocumentLoader().createNewStructuredDocument(model.getBaseLocation(), inputStream, encodingRule); + // TODO: model's not designed for this! + // we want to move to this "set" method, but the 'fire' was needed + // as + // a work around for strucutredModel not handling 'set' right, but + // that 'fireNewDocument' method was causing unbalance + // "aboutToChange" and "changed" + // events. + // model.setStructuredDocument((IStructuredDocument) + // structuredDocument); + // ((IStructuredDocument) + // structuredDocument).fireNewDocument(this); + model.getStructuredDocument().set(structuredDocument.get()); + + } + documentLoaderInstance = null; + + } + + /** + * deprecated -- use EncodingRule form + */ + synchronized public void load(InputStream inputStream, IStructuredModel model, String encodingName, String lineDelimiter) throws UnsupportedEncodingException, java.io.IOException { + // note we don't open the stream, so we don't close it + // TEMP work around to maintain previous function, + // until everyone can change to EncodingRule.FORCE_DEFAULT + if (encodingName != null && encodingName.trim().length() == 0) { + // redirect to new method + load(inputStream, model, EncodingRule.FORCE_DEFAULT); + } + else { + load(inputStream, model, EncodingRule.CONTENT_BASED); + } + } + + public void load(String filename, InputStream inputStream, IStructuredModel model, String junk, String dummy) throws UnsupportedEncodingException, java.io.IOException { + + long memoryUsed = 0; + if (DEBUG) { + memoryUsed = computeMem(); + System.out.println("measuring heap memory for " + filename); //$NON-NLS-1$ + // System.out.println("heap memory used before load: " + + // memoryUsed); + } + + // during an initial load, we expect the olddocument to be empty + // during re-load, however, it would be full. + IEncodedDocument newstructuredDocument = null; + IEncodedDocument oldStructuredDocument = model.getStructuredDocument(); + + // get new document + if (inputStream == null) { + newstructuredDocument = getDocumentLoader().createNewStructuredDocument(); + } + else { + newstructuredDocument = getDocumentLoader().createNewStructuredDocument(filename, inputStream); + } + if (DEBUG) { + long memoryAtEnd = computeMem(); + // System.out.println("heap memory used after loading new + // document: " + memoryAtEnd); + System.out.println(" heap memory implied used by document: " + (memoryAtEnd - memoryUsed)); //$NON-NLS-1$ + } + + + // TODO: need to straighten out IEncodedDocument mess + if (newstructuredDocument instanceof IStructuredDocument) { + transformInstance((IStructuredDocument) oldStructuredDocument, (IStructuredDocument) newstructuredDocument); + } + else { + // we don't really expect this case, just included for safety + oldStructuredDocument.set(newstructuredDocument.get()); + } + // original hack + // model.setStructuredDocument((IStructuredDocument) + // structuredDocument); + // ((IStructuredDocument) structuredDocument).fireNewDocument(this); + documentLoaderInstance = null; + // technicq of future + // model.setStructuredDocument((IStructuredDocument) + // structuredDocument); + // documentLoaderInstance = null; + if (DEBUG) { + long memoryAtEnd = computeMem(); + // System.out.println("heap memory used after setting to model: " + // + memoryAtEnd); + System.out.println(" heap memory implied used by document and model: " + (memoryAtEnd - memoryUsed)); //$NON-NLS-1$ + } + + } + + /** + * required by interface, being declared here abstractly just as another + * reminder. + */ + abstract public IStructuredModel newModel(); + + /** + * There's nothing to do here in abstract class for initializing adapters. + * Subclasses can and should override this method and provide proper + * intialization (For example, to get DOM document and 'getAdapter' on it, + * so that the first node/notifier has the adapter on it.) + */ + protected void preLoadAdapt(IStructuredModel structuredModel) { + + + } + + /** + * Normally, here in the abstact class, there's nothing to do, but we will + * reset text, since this MIGHT end up being called to recover from error + * conditions (e.g. IStructuredDocument exceptions) And, can be called by + * subclasses. + */ + public IStructuredModel reinitialize(IStructuredModel model) { + // Note: the "minimumization" routines + // of 'replaceText' allow many old nodes to pass through, when + // really its assumed they are created anew. + // so we need to use 'setText' (I think "setText' ends up + // throwing a 'newModel' event though, that may have some + // implications. + model.getStructuredDocument().setText(this, model.getStructuredDocument().get()); + return model; + } + + /** + * This method gets a fresh copy of the data, and repopulates the models + * ... by a call to setText on the structuredDocument. This method is + * needed in some cases where clients are sharing a model and then changes + * canceled. Say for example, one editor and several "displays" are + * sharing a model, if the editor is closed without saving changes, then + * the displays still need a model, but they should revert to the original + * unsaved version. + */ + synchronized public void reload(InputStream inputStream, IStructuredModel structuredModel) { + documentLoaderInstance = null; + try { + // temp solution ... we should be able to do better (more + // efficient) in future. + // Adapters will (probably) need to be sensitive to the fact that + // the document instance changed + // (by being life cycle listeners) + load(inputStream, structuredModel, EncodingRule.CONTENT_BASED); + + // // Note: we apparently read the data (and encoding) correctly + // // before, we just need to make sure we followed the same rule + // as + // // before. + // EncodingMemento previousMemento = + // structuredModel.getStructuredDocument().getEncodingMemento(); + // EncodingRule previousRule = previousMemento.getEncodingRule(); + // //IFile file = ResourceUtil.getFileFor(structuredModel); + // // Note: there's opportunity here for some odd behavior, if the + // // settings have changed from the first load to the reload. + // But, + // // hopefully, + // // will result in the intended behavior. + // Reader allTextReader = + // getDocumentLoader().readInputStream(inputStream, previousRule); + // + // // TODO: avoid use of String instance + // getDocumentLoader().reload(structuredModel.getStructuredDocument(), + // allTextReader); + // // and now "reset" encoding memento to keep it current with the + // // one + // // that was just determined. + // structuredModel.getStructuredDocument().setEncodingMemento(getDocumentLoader().getEncodingMemento()); + // structuredModel.setDirtyState(false); + // StructuredTextUndoManager undoMgr = + // structuredModel.getUndoManager(); + // if (undoMgr != null) { + // undoMgr.reset(); + // } + } + catch (UnsupportedEncodingException e) { + // couldn't happen. The program has apparently + // read the model once, and there'd be no reason the encoding + // could not be used again. + Logger.logException("Warning: XMLLoader::reload. This exception really should not have happened!! But will attemp to continue after dumping stack trace", e); //$NON-NLS-1$ + throw new Error("Program Error", e); //$NON-NLS-1$ + } + catch (IOException e) { + // couldn't happen. The program has apparently + // read the model once, and there'd be no (common) reason it + // couldn't be loaded again. + Logger.logException("Warning: XMLLoader::reload. This exception really should not have happened!! But will attemp to continue after dumping stack trace", e); //$NON-NLS-1$ + throw new Error("Program Error", e); //$NON-NLS-1$ + } + } + + /** + * this work is done better elsewhere, but done here for this version to + * reduce changes. especially since the need for it should go away once we + * no longer need to re-use old document instance. + */ + private void transformInstance(IStructuredDocument oldInstance, IStructuredDocument newInstance) { + /** + * https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4920 + * + * JSP taglib support broken, correct by duplicating extended setup + * information (BlockTagParser extension, + * StructuredDocumentRegionParser extensions) + */ + RegionParser oldParser = oldInstance.getParser(); + RegionParser newParser = newInstance.getParser().newInstance(); + // Register all of the old StructuredDocumentRegionHandlers on the new + // parser + if (oldParser instanceof StructuredDocumentRegionParserExtension && newParser instanceof StructuredDocumentRegionParserExtension) { + List oldHandlers = ((StructuredDocumentRegionParserExtension) oldParser).getStructuredDocumentRegionHandlers(); + for (int i = 0; i < oldHandlers.size(); i++) { + StructuredDocumentRegionHandler handler = ((StructuredDocumentRegionHandler) oldHandlers.get(i)); + if (handler instanceof StructuredDocumentRegionHandlerExtension) { + /** + * Skip the transferring here, the handler will do this + * after everything else but the source is transferred. + */ + } + else { + ((StructuredDocumentRegionParser) oldParser).removeStructuredDocumentRegionHandler(handler); + ((StructuredDocumentRegionParser) newParser).addStructuredDocumentRegionHandler(handler); + handler.resetNodes(); + } + } + } + // Add any global BlockMarkers to the new parser + if (oldParser instanceof BlockTagParser && newParser instanceof BlockTagParser) { + List oldBlockMarkers = ((BlockTagParser) oldParser).getBlockMarkers(); + for (int i = 0; i < oldBlockMarkers.size(); i++) { + BlockMarker blockMarker = ((BlockMarker) oldBlockMarkers.get(i)); + if (blockMarker.isGlobal()) { + ((BlockTagParser) newParser).addBlockMarker(blockMarker); + } + } + } + + ((BasicStructuredDocument) oldInstance).setParser(newParser); + + ((BasicStructuredDocument) oldInstance).setReParser(newInstance.getReParser().newInstance()); + + if (newInstance.getDocumentPartitioner() instanceof StructuredTextPartitioner) { + StructuredTextPartitioner partitioner = null; + if (oldInstance instanceof IDocumentExtension3 && newInstance instanceof IDocumentExtension3) { + partitioner = ((StructuredTextPartitioner) ((IDocumentExtension3) newInstance).getDocumentPartitioner(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING)); + if (partitioner != null) { + partitioner = (StructuredTextPartitioner) partitioner.newInstance(); + } + ((IDocumentExtension3) oldInstance).setDocumentPartitioner(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, partitioner); + } + if (partitioner == null) { + partitioner = (StructuredTextPartitioner) ((StructuredTextPartitioner) newInstance.getDocumentPartitioner()).newInstance(); + oldInstance.setDocumentPartitioner(partitioner); + } + if (partitioner != null) { + partitioner.connect(oldInstance); + } + } + + String existingLineDelimiter = null; + try { + existingLineDelimiter = newInstance.getLineDelimiter(0); + } + catch (BadLocationException e) { + // if empty file, assume platform default + // TODO: should be using user set preference, per content type? + existingLineDelimiter = System.getProperty("line.separator"); //$NON-NLS-1$ + } + + oldInstance.setLineDelimiter(existingLineDelimiter); //$NON-NLS-1$); + if (newInstance.getEncodingMemento() != null) { + oldInstance.setEncodingMemento((EncodingMemento) newInstance.getEncodingMemento().clone()); + } + + /** + * https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4920 + * + * JSP taglib support broken, correct by duplicating extended setup + * information (BlockTagParser extension, + * StructuredDocumentRegionParser extensions) + */ + if (oldParser instanceof StructuredDocumentRegionParserExtension && newParser instanceof StructuredDocumentRegionParserExtension) { + List oldHandlers = ((StructuredDocumentRegionParserExtension) oldParser).getStructuredDocumentRegionHandlers(); + for (int i = 0; i < oldHandlers.size(); i++) { + StructuredDocumentRegionHandler handler = ((StructuredDocumentRegionHandler) oldHandlers.get(i)); + if (handler instanceof StructuredDocumentRegionHandlerExtension) { + StructuredDocumentRegionHandlerExtension handlerExtension = (StructuredDocumentRegionHandlerExtension) handler; + handlerExtension.setStructuredDocument(oldInstance); + } + } + } + String holdString = newInstance.get(); + newInstance = null; + oldInstance.set(holdString); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java new file mode 100644 index 0000000..a5a653d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/AbstractStructuredModel.java
@@ -0,0 +1,1517 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.model; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.jobs.ILock; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.provisional.DocumentChanged; +import org.eclipse.wst.sse.core.internal.provisional.IModelLifecycleListener; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.events.AboutToBeChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.IModelAboutToBeChangedListener; +import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.internal.provisional.events.NewDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager; +import org.eclipse.wst.sse.core.internal.util.URIResolver; +import org.eclipse.wst.sse.core.internal.util.Utilities; + + +public abstract class AbstractStructuredModel implements IStructuredModel { + + private static final String MODEL_MANAGER_NULL = "Warning: AbstractStructuredModel::close: model manager was null during a close of a model (which should be impossible)"; //$NON-NLS-1$ + + class DirtyStateWatcher implements IStructuredDocumentListener { + + public void newModel(NewDocumentEvent structuredDocumentEvent) { + + // I don't think its safe to assume a new model + // is always "fresh", so we'll leave dirty state + // unchanged; + // but we'll tell everyone about it. + setDirtyState(fDirtyState); + } + + public void noChange(NoChangeEvent structuredDocumentEvent) { + + // don't change dirty state + } + + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent) { + + setDirtyState(true); + // no need to listen any more + if (fStructuredDocument != null) { + fStructuredDocument.removeDocumentChangedListener(fDirtyStateWatcher); + } + } + + public void regionChanged(RegionChangedEvent structuredDocumentEvent) { + + setDirtyState(true); + // no need to listen any more + if (fStructuredDocument != null) { + fStructuredDocument.removeDocumentChangedListener(fDirtyStateWatcher); + } + } + + public void regionsReplaced(RegionsReplacedEvent structuredDocumentEvent) { + + setDirtyState(true); + // no need to listen any more + if (fStructuredDocument != null) { + fStructuredDocument.removeDocumentChangedListener(fDirtyStateWatcher); + } + } + } + + class DocumentToModelNotifier implements IStructuredDocumentListener, IModelAboutToBeChangedListener { + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IModelAboutToBeChangedListener#modelAboutToBeChanged(org.eclipse.wst.sse.core.events.AboutToBeChangedEvent) + */ + public void modelAboutToBeChanged(AboutToBeChangedEvent structuredDocumentEvent) { + // If we didn't originate the change, take note we are about to + // change based on our underlying document changing. + // If we did originate the change, we, or client, should have + // already called aboutToChangeModel. + if (structuredDocumentEvent.getOriginalRequester() != this) { + aboutToChangeModel(); + } + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#newModel(org.eclipse.wst.sse.core.events.NewDocumentEvent) + */ + public void newModel(NewDocumentEvent structuredDocumentEvent) { + // if we didn't originate the change, take note we have changed + if (structuredDocumentEvent.getOriginalRequester() != this) { + changedModel(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#noChange(org.eclipse.wst.sse.core.events.NoChangeEvent) + */ + public void noChange(NoChangeEvent structuredDocumentEvent) { + // if we didn't originate the change, take note we have changed + if (structuredDocumentEvent.getOriginalRequester() != this) { + changedModel(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#nodesReplaced(org.eclipse.wst.sse.core.events.StructuredDocumentRegionsReplacedEvent) + */ + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent) { + // if we didn't originate the change, take note we have changed + if (structuredDocumentEvent.getOriginalRequester() != this) { + changedModel(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#regionChanged(org.eclipse.wst.sse.core.events.RegionChangedEvent) + */ + public void regionChanged(RegionChangedEvent structuredDocumentEvent) { + // if we didn't originate the change, take note we have changed + if (structuredDocumentEvent.getOriginalRequester() != this) { + changedModel(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#regionsReplaced(org.eclipse.wst.sse.core.events.RegionsReplacedEvent) + */ + public void regionsReplaced(RegionsReplacedEvent structuredDocumentEvent) { + // if we didn't originate the change, take note we have changed + if (structuredDocumentEvent.getOriginalRequester() != this) { + changedModel(); + } + } + + } + + private FactoryRegistry factoryRegistry; + private String fBaseLocation; + boolean fDirtyState; + DirtyStateWatcher fDirtyStateWatcher; + DocumentToModelNotifier fDocumentToModelNotifier; + private String fExplicitContentTypeIdentifier; + private String fId; + + private LifecycleNotificationManager fLifecycleNotificationManager; + + private final Object fListenerLock = new byte[0]; + protected ILock fLockObject; + // private String fLineDelimiter; + // private Object fType; + private IModelHandler fModelHandler; + // issue: we should not "hold on" to model manager, can + // easily get with StructuredModelManager.getModelManager(); + // but will need to add more null checks. + private IModelManager fModelManager; + private int fModelStateChanging; + private Object[] fModelStateListeners; + private boolean fNewState = false; + private URIResolver fResolver; + protected IStructuredDocument fStructuredDocument; + /** + * The time stamp of the underlying resource's modification date, at the + * time this model was created, or the last time it was saved. Note: for + * this version, this variable is not set automatically, be needs to be + * managed by client. The FileModelProvider does this for most cases, but + * if client do not use FileModelProvider, they must set this variable + */ + public long fSynchronizationStamp = IResource.NULL_STAMP; + private boolean reinitializationNeeded; + private Object reinitializeStateData; + + /** + * AbstractStructuredModel constructor comment. + */ + public AbstractStructuredModel() { + + super(); + fDirtyStateWatcher = new DirtyStateWatcher(); + fDocumentToModelNotifier = new DocumentToModelNotifier(); + } + + + /** + * This method is just for getting an instance of the model manager of the + * right Impl type, to be used "internally" for making protected calls + * directly to the impl class. + */ + private ModelManagerImpl _getModelManager() { + // TODO_future: redesign so we don't need this 'Impl' version + if (fModelManager == null) { + fModelManager = StructuredModelManager.getModelManager(); + } + + return (ModelManagerImpl) fModelManager; + } + + /** + * This API allows clients to declare that they are about to make a + * "large" change to the model. This change might be in terms of content + * or it might be in terms of the model id or base location. Note that in + * the case of embedded calls, notification to listeners is sent only + * once. Note that the client who is making these changes has the + * responsibility to restore the models state once finished with the + * changes. See getMemento and restoreState. The method + * isModelStateChanging can be used by a client to determine if the model + * is already in a change sequence. + */ + public void aboutToChangeModel() { + + + // notice this is just a public avenue to our protected method + internalAboutToBeChanged(); + } + + + public void aboutToReinitializeModel() { + + + + // notice this is just a public avenue to our protected method + fireModelAboutToBeReinitialized(); + } + + + public void addModelLifecycleListener(IModelLifecycleListener listener) { + + synchronized (fListenerLock) { + if (fLifecycleNotificationManager == null) { + fLifecycleNotificationManager = new LifecycleNotificationManager(); + } + fLifecycleNotificationManager.addListener(listener); + } + } + + public void addModelStateListener(IModelStateListener listener) { + + synchronized (fListenerLock) { + + if (!Utilities.contains(fModelStateListeners, listener)) { + int oldSize = 0; + if (fModelStateListeners != null) { + // normally won't be null, but we need to be sure, for + // first + // time through + oldSize = fModelStateListeners.length; + } + int newSize = oldSize + 1; + Object[] newListeners = new Object[newSize]; + if (fModelStateListeners != null) { + System.arraycopy(fModelStateListeners, 0, newListeners, 0, oldSize); + } + // add listener to last position + newListeners[newSize - 1] = listener; + // + // now switch new for old + fModelStateListeners = newListeners; + } + } + } + + /** + * This lock to lock the small bits of data and operations in the models + * themselves. this lock is "shared" with document, so, eventually, + * changes can be made safely from either side. + * + * @deprecated + */ + protected final void beginLock() { + } + + public void beginRecording(Object requester) { + + beginRecording(requester, null, null); + } + + public void beginRecording(Object requester, int cursorPosition, int selectionLength) { + + beginRecording(requester, null, null, cursorPosition, selectionLength); + } + + public void beginRecording(Object requester, String label) { + + beginRecording(requester, label, null); + } + + public void beginRecording(Object requester, String label, int cursorPosition, int selectionLength) { + + beginRecording(requester, label, null, cursorPosition, selectionLength); + } + + public void beginRecording(Object requester, String label, String description) { + + if (getUndoManager() != null) + getUndoManager().beginRecording(requester, label, description); + } + + public void beginRecording(Object requester, String label, String description, int cursorPosition, int selectionLength) { + + if (getUndoManager() != null) + getUndoManager().beginRecording(requester, label, description, cursorPosition, selectionLength); + } + + /** + * This API allows a client controlled way of notifying all ModelEvent + * listners that the model has been changed. This method is a matched pair + * to aboutToChangeModel, and *must* be called after aboutToChangeModel + * ... or some listeners could be left waiting indefinitely for the + * changed event. So, its suggested that changedModel always be in a + * finally clause. Likewise, a client should never call changedModel + * without calling aboutToChangeModel first. In the case of embedded + * calls, the notification is just sent once. + */ + public void changedModel() { + + + // notice this is just a public avenue to our protected method + internalModelChanged(); + // also note! + // if we've been "changed" by a client, we might still need + // to be re-initialized, so we'll check and handle that here. + // Note only does this provide a solution to some "missed" + // re-inits, in provides a built in way for clients to + // "force" the model to handle itself, by bracketing any + // changes with aboutToChange and changed, the model itself + // will check. But only call re-init if all other pending + // modelChanged states have been handled. + if (fModelStateChanging == 0 && isReinitializationNeeded()) { + reinit(); + } + } + + + /** + * Based on similar method in FileDocumentProvider. It will provide what + * the modificationStamp would be if resetSynchronzationStamp(resource) + * were used, although for this 'compute' API, no changes to the instance + * are made. + */ + public long computeModificationStamp(IResource resource) { + + + long modificationStamp = resource.getModificationStamp(); + IPath path = resource.getLocation(); + if (path == null) { + return modificationStamp; + } + // Note: checking existence of file is a little different than + // impl in + // the FileDocumentProvider. See defect number 223790. + File file = path.toFile(); + if (!file.exists()) { + return modificationStamp; + } + modificationStamp = file.lastModified(); + return modificationStamp; + } + + + /** + * Provides a copy of the model, but a new ID must be provided. The + * principle of this copy is not to copy fields, etc., as is typically + * done in a clone method, but to return a model with the same content in + * the structuredDocument. Note: It is the callers responsibility to + * setBaseLocation, listners, etc., as appropriate. Type and Encoding are + * the only fields set by this method. If the newId provided already exist + * in the model manager, a ResourceInUse exception is thrown. + */ + public IStructuredModel copy(String newId) throws ResourceInUse { + + + IStructuredModel newModel = null; + // this first one should fail, if not, its treated as an error + // If the caller wants to use an existing one, they can call + // getExisting + // after this failure + newModel = getModelManager().getExistingModelForEdit(newId); + if (newModel != null) { + // be sure to release the reference we got "by accident" (and + // no + // longer need) + newModel.releaseFromEdit(); + throw new ResourceInUse(); + } + newModel = getModelManager().copyModelForEdit(getId(), newId); + return newModel; + } + + + /** + * Disable undo management. + */ + public void disableUndoManagement() { + + if (getUndoManager() != null) + getUndoManager().disableUndoManagement(); + } + + /** + * Enable undo management. + */ + public void enableUndoManagement() { + + if (getUndoManager() != null) + getUndoManager().enableUndoManagement(); + } + + /** + * endLock is protected only for a very special purpose. So subclasses can + * call it to end the lock after updates have been made, but before + * notifications are sent + * + * @deprecated + */ + protected final void endLock() { + } + + public void endRecording(Object requester) { + + if (getUndoManager() != null) + getUndoManager().endRecording(requester); + } + + public void endRecording(Object requester, int cursorPosition, int selectionLength) { + + if (getUndoManager() != null) + getUndoManager().endRecording(requester, cursorPosition, selectionLength); + } + + /** + * Informs all registered model state listeners that the the model is + * about to under go a change. This change might be in terms of contents + * or might be in terms of the model's id or base location. + */ + private void fireModelAboutToBeChanged() { + + // we must assign listeners to local variable, since the add and + // remove listner + // methods can change the actual instance of the listener array + // from another thread + if (fModelStateListeners != null) { + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + ((IModelStateListener) holdListeners[i]).modelAboutToBeChanged(this); + } + } + + } + + protected void fireModelAboutToBeReinitialized() { + + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (fModelStateListeners != null) { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelAboutToBeReinitialized"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + // NOTE: trick for transition. We actual use the same + // listeners + // as modelState, but only send this to those that have + // implemented ModelStateExtended. + IModelStateListener listener = (IModelStateListener) holdListeners[i]; + listener.modelAboutToBeReinitialized(this); + } + } + } + + private void fireModelChanged() { + // we must assign listeners + // to local variable, since the add + // and remove listner + // methods can change the actual instance of the listener + // array from another thread + if (fModelStateListeners != null) { + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + try { + ((IModelStateListener) holdListeners[i]).modelChanged(this); + } + // its so criticial that the begin/end arrive in + // pairs, + // if there happends to be an error in one of the + // modelChanged, + // they we want to be sure rest complete ok. + catch (Exception e) { + Logger.logException(e); + } + } + + } + } + + /** + * Informs all registered model state listeners about a change in the + * dirty state of the model. The dirty state is entirely about changes in + * the content of the model (not, for example, about changes to id, or + * base location -- see modelMoved). + */ + protected void fireModelDirtyStateChanged(IStructuredModel element, boolean isDirty) { + + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (fModelStateListeners != null) { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelDirtyStateChanged"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + ((IModelStateListener) holdListeners[i]).modelDirtyStateChanged(element, isDirty); + } + } + } + + protected void fireModelReinitialized() { + + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (fModelStateListeners != null) { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelReinitialized"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + IModelStateListener listener = (IModelStateListener) holdListeners[i]; + listener.modelReinitialized(this); + } + } + } + + /** + * Informs all registered model state listeners about the deletion of a + * model's underlying resource. + */ + protected void fireModelResourceDeleted(IStructuredModel element) { + + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (fModelStateListeners != null) { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelResourceDeleted"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + ((IModelStateListener) holdListeners[i]).modelResourceDeleted(element); + } + } + } + + /** + * Informs all registered model state listeners that the resource + * underlying a model has been moved. This is typically reflected in a + * change to the id, baseLocation, or both. + */ + protected void fireModelResourceMoved(IStructuredModel originalElement, IStructuredModel movedElement) { + + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (fModelStateListeners != null) { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelResourceMoved"); //$NON-NLS-1$ //$NON-NLS-2$ + } + Object[] holdListeners = fModelStateListeners; + for (int i = 0; i < holdListeners.length; i++) { + ((IModelStateListener) holdListeners[i]).modelResourceMoved(originalElement, movedElement); + } + } + } + + public Object getAdapter(Class adapter) { + + return Platform.getAdapterManager().getAdapter(this, adapter); + } + + /** + * @return java.lang.String + */ + public java.lang.String getBaseLocation() { + + return fBaseLocation; + } + + /** + * @see org.eclipse.wst.sse.core.internal.provisional.IStructuredModel#getContentTypeIdentifier() + */ + public String getContentTypeIdentifier() { + if (fExplicitContentTypeIdentifier != null) + return fExplicitContentTypeIdentifier; + return fModelHandler.getAssociatedContentTypeId(); + } + + /** + * + */ + public FactoryRegistry getFactoryRegistry() { + if (factoryRegistry == null) { + factoryRegistry = new FactoryRegistry(); + } + return factoryRegistry; + } + + /** + * The id is the id that the model manager uses to identify this model + * + * @ISSUE - no one should need to know ID, so this should be default access eventually. + * If clients believe they do need ID, be sure to let us know (open a bug). + */ + public String getId() { + + return fId; + } + + public abstract IndexedRegion getIndexedRegion(int offset); + + /** + * Gets the contentTypeDescription. + * + * @return Returns a ContentTypeDescription + */ + public IModelHandler getModelHandler() { + + return fModelHandler; + } + + + public IModelManager getModelManager() { + + return _getModelManager(); + } + + /** + * This function returns the reference count of underlying model. + */ + // TODO: try to refine the design not to use this function + public int getReferenceCount() { + + + if (getModelManager() == null) + return 0; + return getModelManager().getReferenceCount(getId()); + } + + + /** + * This function returns the reference count of underlying model. + */ + // TODO: try to refine the design not to use this function + public int getReferenceCountForEdit() { + + + + if (getModelManager() == null) + return 0; + return getModelManager().getReferenceCountForEdit(getId()); + } + + + /** + * This function returns the reference count of underlying model. + */ + // TODO: try to refine the design not to use this function + public int getReferenceCountForRead() { + + + + if (getModelManager() == null) + return 0; + return getModelManager().getReferenceCountForRead(getId()); + } + + public Object getReinitializeStateData() { + + return reinitializeStateData; + } + + + + public URIResolver getResolver() { + + return fResolver; + } + + + public IStructuredDocument getStructuredDocument() { + + IStructuredDocument result = null; + result = fStructuredDocument; + return result; + } + + /** + * Insert the method's description here. Creation date: (9/7/2001 2:30:26 + * PM) + * + * @return long + */ + public long getSynchronizationStamp() { + + return fSynchronizationStamp; + } + + public IStructuredTextUndoManager getUndoManager() { + + IStructuredTextUndoManager structuredTextUndoManager = null; + IStructuredDocument structuredDocument = getStructuredDocument(); + if (structuredDocument == null) { + structuredTextUndoManager = null; + } + else { + structuredTextUndoManager = structuredDocument.getUndoManager(); + } + return structuredTextUndoManager; + } + + public void initId(String id) { + fId = id; + } + + private final Object STATE_LOCK = new Object(); + + final protected void internalAboutToBeChanged() { + int state = 0; + // we always increment counter, for every request (so *must* receive + // corresponding number of 'changedModel' requests) + synchronized (STATE_LOCK) { + state = fModelStateChanging++; + } + + // notice we only fire this event if we are not + // already in a model state changing sequence + if (state == 0) { + + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelAboutToBeChanged"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + try { + fireModelAboutToBeChanged(); + } + catch (Exception e) { + Logger.logException("Exception while notifying model state listers of about to change", e); //$NON-NLS-1$ + } + + } + + } + + /** + * Informs all registered model state listeners that an impending change + * is now complete. This method must only be called by 'modelChanged' + * since it keeps track of counts. + */ + final protected void internalModelChanged() { + int state = 0; + // always decrement + synchronized (STATE_LOCK) { + state = --fModelStateChanging; + } + + // Check integrity + // to be less than zero is a programming error, + // but we'll reset to zero + // and try to continue + if (state < 0) { + state = 0; + throw new IllegalStateException("Program Error: modelStateChanging was less than zero"); //$NON-NLS-1$ + } + + + // We only fire this event if all pending requests are done. + // That is, if we've received the same number of modelChanged as + // we have aboutToChangeModel. + if (state == 0) { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "IModelStateListener event for " + getId() + " : modelChanged"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + fireModelChanged(); + } + } + + public boolean isDirty() { + + return fDirtyState; + } + + /** + * This method has very special purpose, its used in subclass + * 'changedModel' to know when to do "ending" sorts of things, right + * before a call to super.ChangedModel would in deed put the model in + * 'end' state. Put another way, at the beginning of the subclasses's + * changedModel, the isModelStateChanging is true, but at end, it will be + * false. So, this method allows a small "peek ahead". + */ + protected boolean isModelChangeStateOnVergeOfEnding() { + + + return fModelStateChanging == 1; + } + + /** + * This method can be called to determine if the model is within a + * "aboutToChange" and "changed" sequence. + */ + public boolean isModelStateChanging() { + + + return fModelStateChanging > 0; + } + + public boolean isNew() { + + return fNewState; + } + + public boolean isReinitializationNeeded() { + + return reinitializationNeeded; + } + + public boolean isSaveNeeded() { + + + if (!isSharedForEdit()) + return isDirty(); + else + return false; + } + + + /** + * This function returns true if there are other references to the + * underlying model. + */ + public boolean isShared() { + if (getModelManager() == null) + return false; + return getModelManager().isShared(getId()); + } + + + /** + * This function returns true if there are other references to the + * underlying model. + */ + public boolean isSharedForEdit() { + + + if (getModelManager() == null) + return false; + return getModelManager().isSharedForEdit(getId()); + } + + + /** + * This function returns true if there are other references to the + * underlying model. + */ + public boolean isSharedForRead() { + + + if (getModelManager() == null) + return false; + return getModelManager().isSharedForRead(getId()); + } + + + public void modelReinitialized() { + + + // notice this is just a public avenue to our protected method + fireModelReinitialized(); + } + + public IStructuredModel newInstance() throws IOException { + + IStructuredModel newModel = null; + // we delegate to the model manager, so loader, etc., can be + // used. + newModel = getModelManager().createNewInstance(this); + return newModel; + } + + public IStructuredModel reinit() { + + + IStructuredModel result = null; + if (fModelStateChanging == 0) { + try { + aboutToChangeModel(); + aboutToReinitializeModel(); + result = _getModelManager().reinitialize(this); + } + finally { + setReinitializeNeeded(false); + setReinitializeStateData(null); + modelReinitialized(); + changedModel(); + } + } + else { + if (Logger.DEBUG_MODELSTATE) { + Logger.log(Logger.INFO, "indeed!!!"); //$NON-NLS-1$ + } + } + return result; + } + + + /** + * This function allows the model to free up any resources it might be + * using. In particular, itself, as stored in the IModelManager. + */ + public void releaseFromEdit() { + + + if (getModelManager() == null) { + throw new IllegalStateException(MODEL_MANAGER_NULL); //$NON-NLS-1$ + } + else { + /* + * Be sure to check the shared state before releasing. (Since + * isShared assumes a count of 1 means not shared ... and we want + * our '1' to be that one.) The problem, of course, is that + * between pre-cycle notification and post-release notification, + * the model could once again have become shared, rendering the + * release notification incorrect. + */ + boolean isShared = isShared(); + + if (!isShared) { + signalPreLifeCycleEventRelease(this); + } + + _getModelManager().releaseFromEdit(this); + if (!isShared) { + signalPostLifeCycleListenerRelease(this); + } + } + + } + + /** + * This function allows the model to free up any resources it might be + * using. In particular, itself, as stored in the IModelManager. + */ + public void releaseFromRead() { + + if (getModelManager() == null) { + throw new IllegalStateException(MODEL_MANAGER_NULL); //$NON-NLS-1$ + } + else { + /* + * Be sure to check the shared state before releasing. (Since + * isShared assumes a count of 1 means not shared ... and we want + * our '1' to be that one.) The problem, of course, is that + * between pre-cycle notification and post-release notification, + * the model could once again have become shared, rendering the + * release notification incorrect. + */ + boolean isShared = isShared(); + + if (!isShared) { + signalPreLifeCycleEventRelease(this); + } + + _getModelManager().releaseFromRead(this); + + if (!isShared) { + signalPostLifeCycleListenerRelease(this); + } + } + } + + + /** + * This function replenishes the model with the resource without saving + * any possible changes. It is used when one editor may be closing, and + * specifially says not to save the model, but another "display" of the + * model still needs to hang on to some model, so needs a fresh copy. + */ + public IStructuredModel reload(InputStream inputStream) throws IOException { + IStructuredModel result = null; + try { + aboutToChangeModel(); + result = _getModelManager().reloadModel(getId(), inputStream); + } + catch (UnsupportedEncodingException e) { + // log for now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + finally { + changedModel(); + } + return result; + } + + public void removeModelLifecycleListener(IModelLifecycleListener listener) { + + // if manager is null, then none have been added, so + // no need to remove any + if (fLifecycleNotificationManager == null) + return; + synchronized (fListenerLock) { + fLifecycleNotificationManager.removeListener(listener); + } + } + + + public void removeModelStateListener(IModelStateListener listener) { + + if (listener == null) + return; + if (fModelStateListeners == null) + return; + // if its not in the listeners, we'll ignore the request + synchronized (fListenerLock) { + if (Utilities.contains(fModelStateListeners, listener)) { + int oldSize = fModelStateListeners.length; + int newSize = oldSize - 1; + Object[] newListeners = new Object[newSize]; + int index = 0; + for (int i = 0; i < oldSize; i++) { + if (fModelStateListeners[i] == listener) { // ignore + } + else { + // copy old to new if its not the one we are + // removing + newListeners[index++] = fModelStateListeners[i]; + } + } + // now that we have a new array, let's switch it for the + // old + // one + fModelStateListeners = newListeners; + } + } + } + + + /** + * A method that modifies the model's synchronization stamp to match the + * resource. Turns out there's several ways of doing it, so this ensures a + * common algorithm. + */ + public void resetSynchronizationStamp(IResource resource) { + + + setSynchronizationStamp(computeModificationStamp(resource)); + } + + + /** + * This API allows a client to initiate notification to all interested + * parties that a model's underlying resource has been deleted. + */ + public void resourceDeleted() { + + + // notice this is just a public avenue to our protected method + fireModelResourceDeleted(this); + } + + + /** + * This method allows a model client to initiate notification to all + * interested parties that a model's underlying resource location has + * changed. Note: we assume caller has already changed baseLocation, Id, + * etc., since its really up to the client to determine what's "new" about + * a moved model. Caution: 'this' and 'newModel' may be the same object. + * This is the case for current working with FileModelProvider, but have + * left the dual argument for future possibilities. + */ + public void resourceMoved(IStructuredModel newModel) { + + + // notice this is just a public avenue to our protected method + fireModelResourceMoved(this, newModel); + } + + + public void save() throws UnsupportedEncodingException, IOException, CoreException { + + int type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.PRE_EVENT; + ModelLifecycleEvent modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + + try { + String stringId = getId(); + _getModelManager().saveModel(stringId, EncodingRule.CONTENT_BASED); + } + + finally { + // we put end notification in finally block, so even if + // error occurs during save, listeners are still notified, + // since their code could depend on receiving, to clean up + // some state, or coordinate other resources. + type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.POST_EVENT; + modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + + public void save(EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + + int type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.PRE_EVENT; + ModelLifecycleEvent modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + + try { + String stringId = getId(); + _getModelManager().saveModel(stringId, encodingRule); + } + finally { + // we put end notification in finally block, so even if + // error occurs during save, listeners are still notified, + // since their code could depend on receiving, to clean up + // some state, or coordinate other resources. + type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.POST_EVENT; + modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + + public void save(IFile iFile) throws UnsupportedEncodingException, IOException, CoreException { + + int type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.PRE_EVENT; + ModelLifecycleEvent modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + + try { + String stringId = getId(); + _getModelManager().saveModel(iFile, stringId, EncodingRule.CONTENT_BASED); + } + + finally { + // we put end notification in finally block, so even if + // error occurs during save, listeners are still notified, + // since their code could depend on receiving, to clean up + // some state, or coordinate other resources. + type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.POST_EVENT; + modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + + public void save(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + + int type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.PRE_EVENT; + ModelLifecycleEvent modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + + try { + String stringId = getId(); + _getModelManager().saveModel(iFile, stringId, encodingRule); + } + finally { + // we put end notificatioon in finally block, so even if + // error occurs during save, listeners are still notified, + // since their code could depend on receiving, to clean up + // some state, or coordinate other resources. + type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.POST_EVENT; + modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + + public void save(OutputStream outputStream) throws UnsupportedEncodingException, CoreException, IOException { + + int type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.PRE_EVENT; + ModelLifecycleEvent modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + + try { + String stringId = getId(); + _getModelManager().saveModel(stringId, outputStream, EncodingRule.CONTENT_BASED); + } + + finally { + // we put end notification in finally block, so even if + // error occurs during save, listeners are still notified, + // since their code could depend on receiving, to clean up + // some state, or coordinate other resources. + type = ModelLifecycleEvent.MODEL_SAVED | ModelLifecycleEvent.POST_EVENT; + modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + + /** + * This attribute is typically used to denote the model's underlying + * resource. + */ + public void setBaseLocation(java.lang.String newBaseLocation) { + fBaseLocation = newBaseLocation; + if (fResolver != null) { + fResolver.setFileBaseLocation(newBaseLocation); + } + } + + public void setContentTypeIdentifier(String contentTypeIdentifier) { + fExplicitContentTypeIdentifier = contentTypeIdentifier; + } + + /** + * + */ + public void setDirtyState(boolean dirtyState) { + + // no need to process (set or fire event), if same value + if (fDirtyState != dirtyState) { + // pre-change notification + int type = ModelLifecycleEvent.MODEL_DIRTY_STATE | ModelLifecycleEvent.PRE_EVENT; + ModelLifecycleEvent modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + + + // the actual change + fDirtyState = dirtyState; + + // old notification + // TODO: C3 remove old notification + if (fDirtyState == false) { + // if we are being set to not dirty (such as just been saved) + // then we need to start listening for changes + // again to know when to set state to true; + getStructuredDocument().addDocumentChangedListener(fDirtyStateWatcher); + } + fireModelDirtyStateChanged(this, dirtyState); + + + // post change notification + type = ModelLifecycleEvent.MODEL_DIRTY_STATE | ModelLifecycleEvent.POST_EVENT; + modelLifecycleEvent = new ModelLifecycleEvent(this, type); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + /** + * @deprecated - will likely be deprecated soon, in favor of direct 'adds' + * ... but takes some redesign. + */ + public void setFactoryRegistry(FactoryRegistry factoryRegistry) { + this.factoryRegistry = factoryRegistry; + } + + /** + * The id is the id that the model manager uses to identify this model. If + * it is being set here, it means the model manger is already managing the + * model with another id, so we have to keep it in sync. This method calls + * notifies listners, if they haven't been notified already, that a "model + * state change" is about to occur. + */ + public void setId(String newId) throws ResourceInUse { + + + // It makes no sense, I don't think, to have an id of null, so + // we'll throw an illegal argument exception if someone trys. Note: + // the IModelManager could not manage a model with an id of null, + // since it uses hashtables, and you can't have a null id for a + // hashtable. + if (newId == null) + throw new IllegalArgumentException(SSECoreMessages.A_model_s_id_can_not_be_nu_EXC_); //$NON-NLS-1$ = "A model's id can not be null" + // To guard against throwing a spurious ResourceInUse exception, + // which can occur when two pieces of code both want to change the id, + // so the second request is spurious, we'll ignore any requests that + // attempt to change the id to what it already is ... note, we use + // 'equals', not identity ('==') so that things like + // strings can be used. This is the same criteria that ids are + // found in model manager -- well, actually, I just checked, and for + // the hashtable impl, the criteria uses .equals AND the condition + // that the hash values be identical (I'm assuming this is always + // true, if equals is true, for now, I'm not sure + // we can assume that hashtable will always be used, but in + // general, should match.) + // + if (newId.equals(fId)) + return; + // we must guard against reassigning an id to one that we already + // are managing. + if (getModelManager() != null) { + boolean inUse = ((ModelManagerImpl)getModelManager()).isIdInUse(newId); + if (inUse) { + throw new ResourceInUse(); + } + } + try { + // normal code path + aboutToChangeModel(); + String oldId = fId; + fId = newId; + if (getModelManager() != null) { + // if managed and the id has changed, notify to + // IModelManager + // TODO: try to refine the design not to do that + if (oldId != null && newId != null && !newId.equals(oldId)) { + getModelManager().moveModel(oldId, newId); + } + } + } + finally { + // make sure this finally is only executed if 'about to Change + // model' has + // been executed. + changedModel(); + } + } + + /** + * Sets the contentTypeDescription. + * + * @param contentTypeDescription + * The contentTypeDescription to set + */ + public void setModelHandler(IModelHandler modelHandler) { + + // no need to fire events if modelHandler has been null + // for this model -- + // this is an attempt at initialization optimization and may need + // to change in future. + boolean trueChange = false; + if (fModelHandler != null) + trueChange = true; + if (trueChange) { + internalAboutToBeChanged(); + } + fModelHandler = modelHandler; + if (trueChange) { + internalModelChanged(); + } + } + + + + public void setModelManager(IModelManager newModelManager) { + + fModelManager = newModelManager; + } + + /** + * + */ + public void setNewState(boolean newState) { + + fNewState = newState; + } + + /** + * Sets a "flag" that reinitialization is needed. + */ + public void setReinitializeNeeded(boolean needed) { + + reinitializationNeeded = needed; + } + + /** + * Holds any data that the reinit procedure might find useful in + * reinitializing the model. This is handy, since the reinitialization may + * not take place at once, and some "old" data may be needed to properly + * undo previous settings. Note: the parameter was intentionally made to + * be of type 'Object' so different models can use in different ways. + */ + public void setReinitializeStateData(Object object) { + + reinitializeStateData = object; + } + + + public void setResolver(URIResolver newResolver) { + + fResolver = newResolver; + } + + + public void setStructuredDocument(IStructuredDocument newStructuredDocument) { + boolean lifeCycleNotification = false; + if (fStructuredDocument != null) { + fStructuredDocument.removeDocumentChangedListener(fDirtyStateWatcher); + fStructuredDocument.removeDocumentAboutToChangeListener(fDocumentToModelNotifier); + fStructuredDocument.removeDocumentChangedListener(fDocumentToModelNotifier); + // prechange notification + lifeCycleNotification = true; + ModelLifecycleEvent modelLifecycleEvent = new DocumentChanged(ModelLifecycleEvent.PRE_EVENT, this, fStructuredDocument, newStructuredDocument); + signalLifecycleEvent(modelLifecycleEvent); + } + + // hold for life cycle notification + IStructuredDocument previousDocument = fStructuredDocument; + // the actual change + fStructuredDocument = newStructuredDocument; + + + // at the super class level, we'll listen for structuredDocument + // changes + // so we can set our dirty state flag + if (fStructuredDocument != null) { + fStructuredDocument.addDocumentChangedListener(fDirtyStateWatcher); + fStructuredDocument.addDocumentAboutToChangeListener(fDocumentToModelNotifier); + fStructuredDocument.addDocumentChangedListener(fDocumentToModelNotifier); + } + + if (lifeCycleNotification) { + // post change notification + ModelLifecycleEvent modelLifecycleEvent = new DocumentChanged(ModelLifecycleEvent.POST_EVENT, this, previousDocument, newStructuredDocument); + signalLifecycleEvent(modelLifecycleEvent); + } + } + + /** + * Insert the method's description here. Creation date: (9/7/2001 2:30:26 + * PM) + * + * @param newSynchronizationStamp + * long + */ + protected void setSynchronizationStamp(long newSynchronizationStamp) { + + fSynchronizationStamp = newSynchronizationStamp; + } + + public void setUndoManager(IStructuredTextUndoManager undoManager) { + + IStructuredDocument structuredDocument = getStructuredDocument(); + if (structuredDocument == null) { + throw new IllegalStateException("document was null when undo manager set on model"); //$NON-NLS-1$ + } + structuredDocument.setUndoManager(undoManager); + } + + /** + * To be called only by "friendly" classes, such as ModelManager, and + * subclasses. + */ + void signalLifecycleEvent(ModelLifecycleEvent event) { + if (fLifecycleNotificationManager == null) + return; + fLifecycleNotificationManager.signalLifecycleEvent(event); + } + + private void signalPostLifeCycleListenerRelease(IStructuredModel structuredModel) { + int type = ModelLifecycleEvent.MODEL_RELEASED | ModelLifecycleEvent.POST_EVENT; + // what's wrong with this design that a cast is needed here!? + ModelLifecycleEvent event = new ModelLifecycleEvent(structuredModel, type); + ((AbstractStructuredModel) structuredModel).signalLifecycleEvent(event); + } + + private void signalPreLifeCycleEventRelease(IStructuredModel structuredModel) { + int type = ModelLifecycleEvent.MODEL_RELEASED | ModelLifecycleEvent.PRE_EVENT; + // what's wrong with this design that a cast is needed here!? + ModelLifecycleEvent event = new ModelLifecycleEvent(structuredModel, type); + ((AbstractStructuredModel) structuredModel).signalLifecycleEvent(event); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/FactoryRegistry.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/FactoryRegistry.java new file mode 100644 index 0000000..d0303903 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/FactoryRegistry.java
@@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.model; + + + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; + + +/** + * This class simply maintains the list of factories and returns singleton + * instances of them. Some "built in" types are automatically created form + * FactoryConfig, if not found registerd, but normally clients can/should + * register their own factories. + * + * Not intended for clients to subclass or instantiate. + * + */ +public final class FactoryRegistry { + + private List factories; + + /** + * intentionally default access + */ + FactoryRegistry() { + super(); + + } + + private List _getFactories() { + + if (factories == null) { + // may need to use java.util.Collections.synchronizedList() if + // syncronization becomes + // necessary (and if so, remember to synchronize on factories) + factories = new ArrayList(); + } + return factories; + + } + + public void addFactory(INodeAdapterFactory factory) { + _getFactories().add(factory); + } + + public void clearFactories() { + factories.clear(); + } + + /* + * @see FactoryRegistry#contains(Object) + */ + public boolean contains(Object type) { + boolean result = false; + // note: we're not using cloned list, so strictly speaking + // is not thread safe. + List internalList = _getFactories(); + for (int i = 0; i < internalList.size(); i++) { + INodeAdapterFactory factory = (INodeAdapterFactory) internalList.get(i); + if (factory.isFactoryForType(type)) { + result = true; + break; + } + } + return result; + } + + /** + * Returns a shallow copy of the list of factories in the registry. Note: + * this can not be used to add/remove factories. Its primarily provided + * for those few cases where a list of factories must be copied from one + * model and added to another. + */ + public List getFactories() { + // note: for object integrity, we don't let anyone get + // our main list (so they have to add through addFactory), + // but we will return a shallow "cloned" list. + List factoryList = new ArrayList(_getFactories()); + return factoryList; + } + + /** + * This method is a not a pure resistry. Factories retrieved based on + * their response to "isFactoryForType(type)". Note that if there is more + * than one factory that can answer 'true' that the most recently added + * factory is used. + */ + public INodeAdapterFactory getFactoryFor(Object type) { + + INodeAdapterFactory result = null; + if (factories == null) + return null; + int listSize = factories.size(); + for (int i = listSize - 1; i >= 0; i--) { + // It is the adapter factories responsibility to answer + // isFactoryForType so it gets choosen. + // Notice we are going through the list backwards to get the + // factory added last. + INodeAdapterFactory a = (INodeAdapterFactory) factories.get(i); + if (a.isFactoryForType(type)) { + result = a; + break; + } + } + return result; + + } + + /** + * + */ + public void release() { + // modified to work on copy of list, for V5PTF1 + // send release to local copy of list + // of factories, since some factories, during + // their release function, may remove + // themselves from the registry. + List localList = getFactories(); + for (int i = 0; i < localList.size(); i++) { + INodeAdapterFactory a = (INodeAdapterFactory) localList.get(i); + // To help bullet proof code, we'll catch and log + // any messages thrown by factories during release, + // but we'll attempt to keep going. + // In nearly all cases, though, such errors are + // severe for product/client, and need to be fixed. + try { + a.release(); + } + catch (Exception e) { + Logger.logException("Program problem releasing factory" + a, e); //$NON-NLS-1$ + } + } + } + + /** + * Removes a factory if it can be retrieved by getFactoryFor(type). If + * there is more than one, all are removed. If there is none, the call + * simply returns (that is, it is not considered an error). + */ + public void removeFactoriesFor(java.lang.Object type) { + if (factories != null) { + int listSize = factories.size(); + // we'll go backwards through list, since we're removing, so + // 'size' change won't matter. + // Note: I'm assuming other items in the collection do not change + // position + // simply because another was removed. + for (int i = listSize - 1; i >= 0; i--) { + // It is the adapter factories responsibility to answer + // isFactoryForType so it gets choosen. + INodeAdapterFactory a = (INodeAdapterFactory) factories.get(i); + if (a.isFactoryForType(type)) { + factories.remove(a); + } + } + } + } + + public void removeFactory(INodeAdapterFactory factory) { + _getFactories().remove(factory); + + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/LifecycleNotificationManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/LifecycleNotificationManager.java new file mode 100644 index 0000000..adcf010 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/LifecycleNotificationManager.java
@@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.model; + +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.IModelLifecycleListener; +import org.eclipse.wst.sse.core.internal.util.Utilities; + + +/** + * For "internal use" only by AbstractStructuredModel + */ + +class LifecycleNotificationManager { + private Object[] fListeners; + + LifecycleNotificationManager() { + super(); + } + + /** + * Adds a new copy of the given listener to the list of Life Cycle + * Listeners. + * + * Multiple copies of the same listener are allowed. This is required to + * support threaded listener management properly and for model-driven move + * to work. For example, two adds and a single remove should result in the + * listener still listening for events. + * + * @param listener + */ + void addListener(IModelLifecycleListener listener) { + if (Logger.DEBUG && Utilities.contains(fListeners, listener)) { + Logger.log(Logger.WARNING, "IModelLifecycleListener " + listener + " listening more than once"); //$NON-NLS-1$ //$NON-NLS-2$ + } + int oldSize = 0; + if (fListeners != null) { + // normally won't be null, but we need to be sure, for first + // time through + oldSize = fListeners.length; + } + int newSize = oldSize + 1; + Object[] newListeners = new Object[newSize]; + if (fListeners != null) { + System.arraycopy(fListeners, 0, newListeners, 0, oldSize); + } + // add listener to last position + newListeners[newSize - 1] = listener; + // + // now switch new for old + fListeners = newListeners; + } + + /** + * Removes a single copy of the given listener from the list of Life Cycle + * Listeners. + * + * @param listener + */ + void removeListener(IModelLifecycleListener listener) { + if (Utilities.contains(fListeners, listener)) { + // if its not in the listeners, we'll ignore the request + int oldSize = fListeners.length; + int newSize = oldSize - 1; + Object[] newListeners = new Object[newSize]; + int index = 0; + boolean removedOnce = false; + for (int i = 0; i < oldSize; i++) { + if (fListeners[i] == listener && !removedOnce) { + // ignore on the first match + removedOnce = true; + } else { + // copy old to new if it's not the one we are removing + newListeners[index++] = fListeners[i]; + } + } + // now that we have a new array, let's switch it for the old + // one + fListeners = newListeners; + } + if (Logger.DEBUG && Utilities.contains(fListeners, listener)) { + Logger.log(Logger.WARNING, "IModelLifecycleListener " + listener + " removed once but still listening"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + void signalLifecycleEvent(ModelLifecycleEvent event) { + if (Logger.DEBUG_LIFECYCLE) { + Logger.log(Logger.INFO, "ModelLifecycleEvent fired for " + event.getModel().getId() + ": " + event.toString()); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println("ModelLifecycleEvent fired for " + event.getModel().getId() + ": " + event.toString()); //$NON-NLS-1$ //$NON-NLS-2$ + } + // We must assign listeners to local variable, since the add and + // remove listener methods can change the actual instance of the + // listener array from another thread + if (fListeners != null) { + Object[] holdListeners = fListeners; + for (int i = 0; i < holdListeners.length; i++) { + IModelLifecycleListener listener = (IModelLifecycleListener) holdListeners[i]; + // only one type of listener for now ... this could become + // more complex + if ((event.getInternalType() & ModelLifecycleEvent.PRE_EVENT) == ModelLifecycleEvent.PRE_EVENT) { + listener.processPreModelEvent(event); + } + if ((event.getInternalType() & ModelLifecycleEvent.POST_EVENT) == ModelLifecycleEvent.POST_EVENT) { + listener.processPostModelEvent(event); + } + } + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/ModelLifecycleEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/ModelLifecycleEvent.java new file mode 100644 index 0000000..def66d0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/ModelLifecycleEvent.java
@@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.model; + +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; + +/** + * This is an early version of a class that may change over the next few + * milestones. + */ + + +public class ModelLifecycleEvent { + + + // this list is for "public" consumption + public static final int MODEL_SAVED = 0x0001; + public static final int MODEL_RELEASED = 0x00002; + public static final int MODEL_DOCUMENT_CHANGED = 0x0003; + public static final int MODEL_DIRTY_STATE = 0x0004; + public static final int MODEL_REVERT= 0x0005; + + // TODO: finish support for these + // following not implemented yet + public static final int MODEL_REINITIALIZED = 0x0006; + //public static final int ADAPTERS_NOTIFIED = 0x0007; + //public static final int MODEL_RESOURCE_MOVED = 0x0008; + //public static final int MODEL_RESOURCE_DELETED = 0x0009; + + // This list (upper two bytes) is for only internal mechanisms and + // subclasses + // For simplicity they are "masked out" when client calls getType() + protected static final int PRE_EVENT = 0x0100; + private final static int MASK = 0x00FF; + protected static final int POST_EVENT = 0x0200; + + + private IStructuredModel fModel; + private int fType; + + public ModelLifecycleEvent() { + super(); + } + + public ModelLifecycleEvent(int type) { + this(); + fType = type; + } + + public ModelLifecycleEvent(IStructuredModel structuredModel, int type) { + this(type); + fModel = structuredModel; + } + + private String debugString(int type) { + String result = null; + switch (type & MASK) { + case MODEL_SAVED : + result = "MODEL_SAVED"; //$NON-NLS-1$ + break; + case MODEL_RELEASED : + result = "MODEL_RELEASED"; //$NON-NLS-1$ + break; + case MODEL_DOCUMENT_CHANGED : + result = "MODEL_DOCUMENT_CHANGED"; //$NON-NLS-1$ + break; + case MODEL_DIRTY_STATE : + result = "MODEL_DIRTY_STATE"; //$NON-NLS-1$ + break; + /* + * case MODEL_REINITIALIZED : result = "MODEL_REINITIALIZED"; + * break; case MODEL_RELOADED : result = "MODEL_RELOADED"; break; + * case ADAPTERS_NOTIFIED : result = "ADAPTERS_NOTIFIED"; break; + * case MODEL_RESOURCE_MOVED : result = "MODEL_RESOURCE_MOVED"; + * break; case MODEL_RESOURCE_DELETED : result = + * "MODEL_RESOURCE_DELETED"; break; + */ + default : + throw new IllegalStateException("ModelLifecycleEvent did not have valid type"); //$NON-NLS-1$ + } + return result; + } + + protected int getInternalType() { + + return fType; + } + + public IStructuredModel getModel() { + + return fModel; + } + + public int getType() { + + // for now, we'll mask type to "public" ones this easy + // way ... but I know there must be a better way + return fType & MASK; + } + + public String toString() { + String result = null; + result = "ModelLifecycleEvent: " + debugString(fType); //$NON-NLS-1$ + return result; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/ModelManagerImpl.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/ModelManagerImpl.java new file mode 100644 index 0000000..82c1854 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/ModelManagerImpl.java
@@ -0,0 +1,2160 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * * Frank Zigler/Web Performance, Inc. - 288196 - Deadlock in ModelManagerImpl after IOException + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.model; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.jobs.ILock; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.wst.sse.core.internal.FileBufferModelManager; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.NullMemento; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.internal.document.DocumentReader; +import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; +import org.eclipse.wst.sse.core.internal.encoding.CodedStreamCreator; +import org.eclipse.wst.sse.core.internal.encoding.CommonEncodingPreferenceNames; +import org.eclipse.wst.sse.core.internal.encoding.ContentBasedPreferenceGateway; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.exceptions.MalformedOutputExceptionWithDetail; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.modelhandler.ModelHandlerRegistry; +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.util.Assert; +import org.eclipse.wst.sse.core.internal.util.ProjectResolver; +import org.eclipse.wst.sse.core.internal.util.URIResolver; +import org.eclipse.wst.sse.core.internal.util.Utilities; + +/** + * <p>Not intended to be subclassed, referenced or instantiated by clients. + * Clients should obtain an instance of the IModelManager interface through + * {@link StructuredModelManager#getModelManager()}.</p> + * + * <p>This class is responsible for creating, retrieving, and caching + * StructuredModels It retrieves the cached objects by an id which is + * typically a String representing the resources URI. Note: Its important that + * all clients that share a resource do so using <b>identical </b> + * identifiers, or else different instances will be created and retrieved, + * even if they all technically point to the same resource on the file system. + * This class also provides a convenient place to register Model Loaders and + * Dumpers based on 'type'.</p> + */ +public class ModelManagerImpl implements IModelManager { + + + + static class ReadEditType { + ReadEditType(String type) { + } + } + + class SharedObject { + int referenceCountForEdit; + int referenceCountForRead; + volatile IStructuredModel theSharedModel; + final ILock LOAD_LOCK = Job.getJobManager().newLock(); + volatile boolean initializing = true; + volatile boolean doWait = true; + // The field 'id' is only meant for debug + final String id; + + SharedObject(String id) { + this.id=id; + // be aware, this lock will leak and cause the deadlock detector to be horrible if we never release it + LOAD_LOCK.acquire(); + } + + /** + * Waits until this shared object has been attempted to be loaded. The + * load is "attempted" because not all loads result in a model. However, + * upon leaving this method, theShareModel variable is up-to-date. + */ + public void waitForLoadAttempt() { + final boolean allowInterrupt = PrefUtil.ALLOW_INTERRUPT_WAITING_THREAD; + final long timeLimit = (PrefUtil.WAIT_DELAY==0) ? Long.MAX_VALUE : PrefUtil.now() + PrefUtil.WAIT_DELAY; + final Job current = Job.getJobManager().currentJob(); + boolean interrupted = false; + try { + while (initializing) { + if (current!=null) { + current.yieldRule(null); + } + try { + loop(); + } catch (InterruptedException e) { + if (allowInterrupt) { + throw new OperationCanceledException("Waiting thread interrupted while waiting for model id: "+id + " to load"); + } else { + interrupted=true; + } + } + if (PrefUtil.now() >= timeLimit ) + throw new OperationCanceledException("Waiting thread timeout exceeded while waiting for model id: "+id + " to load"); + } + } + finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + private void loop() throws InterruptedException { + if (initializing) { + if (LOAD_LOCK.acquire(PrefUtil.WAIT_INTERVAL_MS)) { + // if we got the lock, but initializing is still not true the deadlock detector gave us + // the lock and caused reentrancy into this critical section. This is invalid and the + // sign of a cyclical load attempt. In this case, we through an + // OperationCanceledException in lew of entering a spin-loop. + if (initializing) { + LOAD_LOCK.release(); + throw new OperationCanceledException("Aborted cyclic load attempt for model with id: "+ id ); + } else { + LOAD_LOCK.release(); + } + } + } + } + + /** + * Flags this model as loaded. All waiting methods on + * {@link #waitForLoadAttempt()} will proceed after this method returns. + */ + public void setLoaded() { + initializing = false; + LOAD_LOCK.release(); + } + } + + private Exception debugException = null; + + /** + * Our singleton instance + */ + private static ModelManagerImpl instance; + private final static int READ_BUFFER_SIZE = 4096; + + /** + * Not to be called by clients, will be made restricted access. + * + * @return + */ + public synchronized static IModelManager getInstance() { + + if (instance == null) { + instance = new ModelManagerImpl(); + } + return instance; + } + + /** + * Our cache of managed objects + */ + private Map fManagedObjects; + + private ModelHandlerRegistry fModelHandlerRegistry; + private final ReadEditType READ = new ReadEditType("read"); //$NON-NLS-1$ + private final ReadEditType EDIT = new ReadEditType("edit"); //$NON-NLS-1$ + + private final ILock SYNC = Job.getJobManager().newLock(); + /** + * Intentionally default access only. + * + */ + ModelManagerImpl() { + super(); + fManagedObjects = new HashMap(); + // To prevent deadlocks: always acquire multiple locks in this order: SYNC, sharedObject. + // DO NOT acquire a SYNC within a sharedObject lock, unless you already own the SYNC lock + // Tip: Try to hold the smallest number of locks you can + } + + private IStructuredModel _commonCreateModel(IFile file, String id, IModelHandler handler, URIResolver resolver, ReadEditType rwType, EncodingRule encodingRule) throws IOException,CoreException { + SharedObject sharedObject = null; + + SYNC.acquire(); + sharedObject = (SharedObject) fManagedObjects.get(id); + SYNC.release(); + + while(true) { + if (sharedObject!=null) { + sharedObject.waitForLoadAttempt(); + } + SYNC.acquire(); + // we know this object's model has passed the load, however, we don't know + // it's reference count status. It might have already been disposed. Or it could have + // been disposed and a concurrent thread has already begun loading it, in which case + // we should use the sharedobject they are loading. + // NOTE: This pattern is applied 3 times in this class, but only doc'd once. The logic is + // exactly the same. + SharedObject testObject = (SharedObject) fManagedObjects.get(id); + if (testObject==null) { + // null means it's been disposed, we need to do the work to reload it. + sharedObject = new SharedObject(id); + fManagedObjects.put(id, sharedObject); + SYNC.release(); + _doCommonCreateModel(file, id, handler, resolver, rwType, encodingRule, + sharedObject); + break; + } else if (sharedObject == testObject) { + // if nothing happened, just increment the could and return the shared model + synchronized(sharedObject) { + if (sharedObject.theSharedModel!=null) { + _incrCount(sharedObject, rwType); + } + } + SYNC.release(); + break; + } else { + // sharedObject != testObject which means the object we were waiting on has been disposed + // a replacement has already been placed in the managedObjects table. Through away our + // stale sharedObject and continue on with the one we got from the queue. Note: We don't know its + // state, so continue the waitForLoad-check loop. + SYNC.release(); + sharedObject = testObject; + } + } + + // we expect to always return something + if (sharedObject == null) { + debugException = new Exception("instance only for stack trace"); //$NON-NLS-1$ + Logger.logException("Program Error: no model recorded for id " + id, debugException); //$NON-NLS-1$ + } + + // note: clients must call release for each time they call get. + return sharedObject==null ? null : sharedObject.theSharedModel; + } + + private void _decrCount(SharedObject sharedObject, ReadEditType type) { + if (type == READ) { + sharedObject.referenceCountForRead--; + FileBufferModelManager.getInstance().disconnect(sharedObject.theSharedModel.getStructuredDocument()); + } + else if (type == EDIT) { + sharedObject.referenceCountForEdit--; + FileBufferModelManager.getInstance().disconnect(sharedObject.theSharedModel.getStructuredDocument()); + } + else + throw new IllegalArgumentException(); + } + + private void _doCommonCreateModel(IFile file, String id, IModelHandler handler, + URIResolver resolver, ReadEditType rwType, EncodingRule encodingRule, + SharedObject sharedObject) throws CoreException, IOException { + // XXX: Does not integrate with FileBuffers + boolean doRemove = true; + try { + synchronized(sharedObject) { + InputStream inputStream = null; + IStructuredModel model = null; + try { + model = _commonCreateModel(id, handler, resolver); + IModelLoader loader = handler.getModelLoader(); + inputStream = Utilities.getMarkSupportedStream(file.getContents(true)); + loader.load(Utilities.getMarkSupportedStream(inputStream), model, encodingRule); + } + catch (ResourceInUse e) { + // impossible, since we've already found + handleProgramError(e); + } finally { + if (inputStream!=null) { + try { + inputStream.close(); + } catch(IOException e) { + } + } + } + if (model != null) { + // add to our cache + sharedObject.theSharedModel=model; + _initCount(sharedObject, rwType); + doRemove = false; + } + } + } + finally{ + if (doRemove) { + SYNC.acquire(); + fManagedObjects.remove(id); + SYNC.release(); + } + sharedObject.setLoaded(); + } + } + + private IStructuredModel _commonCreateModel(InputStream inputStream, String id, IModelHandler handler, URIResolver resolver, ReadEditType rwType, String encoding, String lineDelimiter) throws IOException { + + if (id == null) { + throw new IllegalArgumentException("Program Error: id may not be null"); //$NON-NLS-1$ + } + SharedObject sharedObject = null; + + SYNC.acquire(); + sharedObject = (SharedObject) fManagedObjects.get(id); + SYNC.release(); + + while(true) { + if (sharedObject!=null) { + sharedObject.waitForLoadAttempt(); + } + SYNC.acquire(); + SharedObject testObject = (SharedObject) fManagedObjects.get(id); + if (testObject==null) { + // it was removed ,so lets create it + sharedObject = new SharedObject(id); + fManagedObjects.put(id, sharedObject); + SYNC.release(); + _doCommonCreateModel(inputStream, id, handler, resolver, rwType, + encoding, lineDelimiter, sharedObject); + break; + } else if (sharedObject == testObject) { + synchronized(sharedObject) { + if (sharedObject.theSharedModel!=null) { + _incrCount(sharedObject, rwType); + } + } + SYNC.release(); + break; + } else { + SYNC.release(); + sharedObject = testObject; + } + } + + // we expect to always return something + Assert.isNotNull(sharedObject, "Program Error: no model recorded for id " + id); //$NON-NLS-1$ + // note: clients must call release for each time they call get. + return sharedObject.theSharedModel; + + } + + private void _doCommonCreateModel(InputStream inputStream, String id, IModelHandler handler, + URIResolver resolver, ReadEditType rwType, String encoding, String lineDelimiter, + SharedObject sharedObject) throws IOException { + boolean doRemove = true; + try { + synchronized(sharedObject) { + IStructuredModel model = null; + try { + model = _commonCreateModel(id, handler, resolver); + IModelLoader loader = handler.getModelLoader(); + if (inputStream == null) { + Logger.log(Logger.WARNING, "model was requested for id " + id + " without a content InputStream"); //$NON-NLS-1$ //$NON-NLS-2$ + } + loader.load(id, Utilities.getMarkSupportedStream(inputStream), model, encoding, lineDelimiter); + } + catch (ResourceInUse e) { + // impossible, since we've already found + handleProgramError(e); + } + if (model != null) { + /** + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=264228 + * + * Ensure that the content type identifier field of the model + * is properly set. This is normally handled by the + * FileBufferModelManager when working with files as it knows + * the content type in advance; here is where we handle it for + * streams. + */ + if (model instanceof AbstractStructuredModel) { + DocumentReader reader = new DocumentReader(model.getStructuredDocument()); + IContentDescription description = Platform.getContentTypeManager().getDescriptionFor(reader, id, new QualifiedName[0]); + reader.close(); + if (description != null && description.getContentType() != null) { + ((AbstractStructuredModel) model).setContentTypeIdentifier(description.getContentType().getId()); + } + } + + sharedObject.theSharedModel = model; + _initCount(sharedObject, rwType); + doRemove = false; + } + } + } + finally { + if (doRemove) { + SYNC.acquire(); + // remove it if we didn't get one back + fManagedObjects.remove(id); + SYNC.release(); + } + sharedObject.setLoaded(); + } + } + + private IStructuredModel _commonCreateModel(String id, IModelHandler handler, URIResolver resolver) throws ResourceInUse { + + IModelLoader loader = handler.getModelLoader(); + IStructuredModel result = loader.createModel(); + // in the past, id was null for "unmanaged" case, so we won't + // try and set it + if (id != null) { + result.setId(id); + } + result.setModelHandler(handler); + result.setResolver(resolver); + // some obvious redunancy here that maybe could be improved + // in future, but is necessary for now + result.setBaseLocation(id); + if (resolver != null) { + resolver.setFileBaseLocation(id); + } + addFactories(result, handler); + return result; + } + + private IStructuredModel _commonGetModel(IFile iFile, ReadEditType rwType, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + IStructuredModel model = null; + + if (iFile != null && iFile.exists()) { + String id = calculateId(iFile); + IModelHandler handler = calculateType(iFile); + URIResolver resolver = calculateURIResolver(iFile); + model = _commonCreateModel(iFile, id, handler, resolver, rwType, encodingRule); + } + + return model; + } + + private IStructuredModel _commonGetModel(IFile iFile, ReadEditType rwType, String encoding, String lineDelimiter) throws UnsupportedEncodingException, IOException, CoreException { + String id = calculateId(iFile); + IStructuredModel model = _commonGetModel(iFile, id, rwType, encoding, lineDelimiter); + + return model; + } + + private IStructuredModel _commonGetModel(IFile file, String id, ReadEditType rwType, String encoding, String lineDelimiter) throws IOException, CoreException { + if (id == null) + throw new IllegalArgumentException("Program Error: id may not be null"); //$NON-NLS-1$ + + SharedObject sharedObject = null; + if (file != null && file.exists()) { + SYNC.acquire(); + sharedObject = (SharedObject) fManagedObjects.get(id); + SYNC.release(); + + while(true) { + if (sharedObject!=null) { + sharedObject.waitForLoadAttempt(); + } + SYNC.acquire(); + SharedObject testObject = (SharedObject) fManagedObjects.get(id); + if (testObject==null) { + // it was removed ,so lets create it + sharedObject = new SharedObject(id); + fManagedObjects.put(id, sharedObject); + + SYNC.release(); + _doCommonGetModel(file, id, sharedObject,rwType); + break; + } else if (sharedObject == testObject) { + synchronized(sharedObject) { + if (sharedObject.theSharedModel!=null) { + _incrCount(sharedObject, rwType); + } + } + SYNC.release(); + break; + } else { + // we got a different object than what we were expecting + SYNC.release(); + // two threads were interested in models for the same id. + // The other thread one, so lets back off and try again. + sharedObject = testObject; + } + } + } + + // if we don't know how to create a model + // for this type of file, return null + + // note: clients must call release for each time they call + // get. + + return sharedObject==null ? null : sharedObject.theSharedModel; + } + + private void _doCommonGetModel(IFile file, String id, SharedObject sharedObject,ReadEditType rwType) { + boolean doRemove = true; + try { + synchronized(sharedObject) { + sharedObject.doWait=false; + IStructuredModel model = null; + try { + model = FileBufferModelManager.getInstance().getModel(file); + } + finally { + sharedObject.doWait=true; + } + if (model != null) { + sharedObject.theSharedModel=model; + _initCount(sharedObject, rwType); + doRemove = false; + } + } + } + finally { + if (doRemove) { + SYNC.acquire(); + fManagedObjects.remove(id); + SYNC.release(); + } + sharedObject.setLoaded(); + } + } + + private SharedObject _commonNewModel(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException { + IStructuredModel aSharedModel = null; + // First, check if resource already exists on file system. + // if is does, then throw Resource in Use iff force==false + + if (iFile.exists() && !force) { + throw new ResourceAlreadyExists(); + } + + SharedObject sharedObject = null; + String id = calculateId(iFile); + try { + SYNC.acquire(); + + sharedObject = (SharedObject) fManagedObjects.get(id); + + if (sharedObject != null && !force) { + // if in cache already, and force is not true, then this is an + // error + // in call + throw new ResourceInUse(); + } + + sharedObject = new SharedObject(id); + fManagedObjects.put(id, sharedObject); + + } finally { + SYNC.release(); + } + + // if we get to here without above exceptions, then all is ok + // to get model like normal, but set 'new' attribute (where the + // 'new' attribute means this is a model without a corresponding + // underlying resource. + aSharedModel = FileBufferModelManager.getInstance().getModel(iFile); + aSharedModel.setNewState(true); + + sharedObject.theSharedModel=aSharedModel; + // when resource is provided, we can set + // synchronization stamp ... otherwise client should + // Note: one client which does this is FileModelProvider. + aSharedModel.resetSynchronizationStamp(iFile); + return sharedObject; + } + + public IStructuredModel _getModelFor(IStructuredDocument document, ReadEditType accessType) { + + String id = FileBufferModelManager.getInstance().calculateId(document); + if (id == null) { + if (READ == accessType) + return getExistingModelForRead(document); + if (EDIT == accessType) + return getExistingModelForEdit(document); + Assert.isNotNull(id, "unknown IStructuredDocument " + document); //$NON-NLS-1$ + } + + SharedObject sharedObject = null; + SYNC.acquire(); + sharedObject = (SharedObject) fManagedObjects.get(id); + SYNC.release(); + + while(true) { + if (sharedObject!=null) { + sharedObject.waitForLoadAttempt(); + } + SYNC.acquire(); + SharedObject testObject = (SharedObject) fManagedObjects.get(id); + if (testObject==null) { + sharedObject = new SharedObject(id); + fManagedObjects.put(id, sharedObject); + SYNC.release(); + synchronized(sharedObject) { + sharedObject.theSharedModel = FileBufferModelManager.getInstance().getModel(document); + _initCount(sharedObject, accessType); + sharedObject.setLoaded(); + } + break; + } else if (sharedObject == testObject) { + synchronized(sharedObject) { + Assert.isTrue(sharedObject.referenceCountForEdit + sharedObject.referenceCountForRead > 0, "reference count was less than zero"); + if (sharedObject.theSharedModel!=null) { + _incrCount(sharedObject, accessType); + } + } + SYNC.release(); + break; + } else { + SYNC.release(); + sharedObject = testObject; + } + } + + return sharedObject==null ? null : sharedObject.theSharedModel; + } + + private void _incrCount(SharedObject sharedObject, ReadEditType type) { + synchronized(sharedObject) { + if (type == READ) { + sharedObject.referenceCountForRead++; + FileBufferModelManager.getInstance().connect(sharedObject.theSharedModel.getStructuredDocument()); + } + else if (type == EDIT) { + sharedObject.referenceCountForEdit++; + FileBufferModelManager.getInstance().connect(sharedObject.theSharedModel.getStructuredDocument()); + } + else + throw new IllegalArgumentException(); + } + } + + private void _initCount(SharedObject sharedObject, ReadEditType type) { + synchronized(sharedObject) { + if (type == READ) { + FileBufferModelManager.getInstance().connect(sharedObject.theSharedModel.getStructuredDocument()); + sharedObject.referenceCountForRead = 1; + } + else if (type == EDIT) { + FileBufferModelManager.getInstance().connect(sharedObject.theSharedModel.getStructuredDocument()); + sharedObject.referenceCountForEdit = 1; + } + else + throw new IllegalArgumentException(); + } + } + + private void addFactories(IStructuredModel model, IModelHandler handler) { + Assert.isNotNull(model, "model can not be null"); //$NON-NLS-1$ + FactoryRegistry registry = model.getFactoryRegistry(); + Assert.isNotNull(registry, "model's Factory Registry can not be null"); //$NON-NLS-1$ + List factoryList = handler.getAdapterFactories(); + addFactories(model, factoryList); + } + + private void addFactories(IStructuredModel model, List factoryList) { + Assert.isNotNull(model, "model can not be null"); //$NON-NLS-1$ + FactoryRegistry registry = model.getFactoryRegistry(); + Assert.isNotNull(registry, "model's Factory Registry can not be null"); //$NON-NLS-1$ + // Note: we add all of them from handler, even if + // already exists. May need to reconsider this. + if (factoryList != null) { + Iterator iterator = factoryList.iterator(); + while (iterator.hasNext()) { + INodeAdapterFactory factory = (INodeAdapterFactory) iterator.next(); + registry.addFactory(factory); + } + } + } + + + /** + * Calculate id provides a common way to determine the id from the input + * ... needed to get and save the model. It is a simple class utility, but + * is an instance method so can be accessed via interface. + */ + public String calculateId(IFile file) { + return FileBufferModelManager.getInstance().calculateId(file); + } + + private IModelHandler calculateType(IFile iFile) throws CoreException { + // IModelManager mm = ((ModelManagerPlugin) + // Platform.getPlugin(ModelManagerPlugin.ID)).getModelManager(); + ModelHandlerRegistry cr = getModelHandlerRegistry(); + IModelHandler cd = cr.getHandlerFor(iFile); + return cd; + } + + private IModelHandler calculateType(String filename, InputStream inputStream) throws IOException { + ModelHandlerRegistry cr = getModelHandlerRegistry(); + IModelHandler cd = cr.getHandlerFor(filename, inputStream); + return cd; + } + + /** + * + */ + private URIResolver calculateURIResolver(IFile file) { + // Note: see comment in plugin.xml for potentially + // breaking change in behavior. + + IProject project = file.getProject(); + URIResolver resolver = (URIResolver) project.getAdapter(URIResolver.class); + if (resolver == null) + resolver = new ProjectResolver(project); + Object location = file.getLocation(); + if (location == null) + location = file.getLocationURI(); + if (location != null) + resolver.setFileBaseLocation(location.toString()); + return resolver; + } + + /* + * Note: This method appears in both ModelManagerImpl and JSEditor (with + * just a minor difference). They should be kept the same. + * + * @deprecated - handled by platform + */ + private void convertLineDelimiters(IDocument document, IFile iFile) throws CoreException { + // Note: calculateType(iFile) returns a default xml model handler if + // content type is null. + String contentTypeId = calculateType(iFile).getAssociatedContentTypeId(); + String endOfLineCode = ContentBasedPreferenceGateway.getPreferencesString(contentTypeId, CommonEncodingPreferenceNames.END_OF_LINE_CODE); + // endOfLineCode == null means the content type does not support this + // function (e.g. DTD) + // endOfLineCode == "" means no translation + if (endOfLineCode != null && endOfLineCode.length() > 0) { + String lineDelimiterToUse = System.getProperty("line.separator"); //$NON-NLS-1$ + if (endOfLineCode.equals(CommonEncodingPreferenceNames.CR)) + lineDelimiterToUse = CommonEncodingPreferenceNames.STRING_CR; + else if (endOfLineCode.equals(CommonEncodingPreferenceNames.LF)) + lineDelimiterToUse = CommonEncodingPreferenceNames.STRING_LF; + else if (endOfLineCode.equals(CommonEncodingPreferenceNames.CRLF)) + lineDelimiterToUse = CommonEncodingPreferenceNames.STRING_CRLF; + + TextEdit multiTextEdit = new MultiTextEdit(); + int lineCount = document.getNumberOfLines(); + try { + for (int i = 0; i < lineCount; i++) { + IRegion lineInfo = document.getLineInformation(i); + int lineStartOffset = lineInfo.getOffset(); + int lineLength = lineInfo.getLength(); + int lineEndOffset = lineStartOffset + lineLength; + + if (i < lineCount - 1) { + String currentLineDelimiter = document.getLineDelimiter(i); + if (currentLineDelimiter != null && currentLineDelimiter.compareTo(lineDelimiterToUse) != 0) + multiTextEdit.addChild(new ReplaceEdit(lineEndOffset, currentLineDelimiter.length(), lineDelimiterToUse)); + } + } + + if (multiTextEdit.getChildrenSize() > 0) + multiTextEdit.apply(document); + } + catch (BadLocationException exception) { + // just adding generic runtime here, until whole method + // deleted. + throw new RuntimeException(exception.getMessage()); + } + } + } + + /** + * this used to be in loader, but has been moved here + */ + private IStructuredModel copy(IStructuredModel model, String newId) throws ResourceInUse { + IStructuredModel newModel = null; + IStructuredModel oldModel = model; + IModelHandler modelHandler = oldModel.getModelHandler(); + IModelLoader loader = modelHandler.getModelLoader(); + // newModel = loader.newModel(); + newModel = loader.createModel(oldModel); + // newId, oldModel.getResolver(), oldModel.getModelManager()); + newModel.setModelHandler(modelHandler); + // IStructuredDocument oldStructuredDocument = + // oldModel.getStructuredDocument(); + // IStructuredDocument newStructuredDocument = + // oldStructuredDocument.newInstance(); + // newModel.setStructuredDocument(newStructuredDocument); + newModel.setResolver(oldModel.getResolver()); + newModel.setModelManager(oldModel.getModelManager()); + // duplicateFactoryRegistry(newModel, oldModel); + newModel.setId(newId); + // set text of new one after all initialization is done + String contents = oldModel.getStructuredDocument().getText(); + newModel.getStructuredDocument().setText(this, contents); + return newModel; + } + + /** + */ + public IStructuredModel copyModelForEdit(String oldId, String newId) throws ResourceInUse { + IStructuredModel newModel = null; + // get the existing model associated with this id + IStructuredModel model = getExistingModel(oldId); + // if it doesn't exist, ignore request (though this would normally + // be a programming error. + if (model == null) + return null; + SharedObject sharedObject = null; + try { + SYNC.acquire(); + // now be sure newModel does not exist + sharedObject = (SharedObject) fManagedObjects.get(newId); + if (sharedObject != null) { + throw new ResourceInUse(); + } + sharedObject = new SharedObject(newId); + fManagedObjects.put(newId,sharedObject); + } finally { + SYNC.release(); + } + // get loader based on existing type (note the type assumption) + // Object type = ((IStructuredModel) model).getType(); + // IModelHandler type = model.getModelHandler(); + // IModelLoader loader = (IModelLoader) getModelLoaders().get(type); + // IModelLoader loader = (IModelLoader) getModelLoaders().get(type); + // ask the loader to copy + synchronized(sharedObject) { + sharedObject.doWait = false; + newModel = copy(model, newId); + sharedObject.doWait = true; + } + if (newModel != null) { + // add to our cache + synchronized(sharedObject) { + sharedObject.theSharedModel=newModel; + sharedObject.referenceCountForEdit = 1; + trace("copied model", newId, sharedObject.referenceCountForEdit); //$NON-NLS-1$ + } + } else { + SYNC.acquire(); + fManagedObjects.remove(newId); + SYNC.release(); + } + sharedObject.setLoaded(); + return newModel; + } + + /** + * Similar to clone, except the new instance has no content. Note: this + * produces an unmanaged model, for temporary use. If a true shared model + * is desired, use "copy". + */ + public IStructuredModel createNewInstance(IStructuredModel oldModel) throws IOException { + IModelHandler handler = oldModel.getModelHandler(); + IModelLoader loader = handler.getModelLoader(); + IStructuredModel newModel = loader.createModel(oldModel); + newModel.setModelHandler(handler); + if (newModel instanceof AbstractStructuredModel) { + ((AbstractStructuredModel) newModel).setContentTypeIdentifier(oldModel.getContentTypeIdentifier()); + } + URIResolver oldResolver = oldModel.getResolver(); + newModel.setResolver(oldResolver); + try { + newModel.setId(DUPLICATED_MODEL); + } + catch (ResourceInUse e) { + // impossible, since this is an unmanaged model + } + // base location should be null, but we'll set to + // null to be sure. + newModel.setBaseLocation(null); + return newModel; + } + + /** + * Factory method, since a proper IStructuredDocument must have a proper + * parser assigned. Note: its assume that IFile does not actually exist as + * a resource yet. If it does, ResourceAlreadyExists exception is thrown. + * If the resource does already exist, then createStructuredDocumentFor is + * the right API to use. + * + * @throws ResourceInUse + * + */ + public IStructuredDocument createNewStructuredDocumentFor(IFile iFile) throws ResourceAlreadyExists, IOException, CoreException { + if (iFile.exists()) { + throw new ResourceAlreadyExists(iFile.getFullPath().toOSString()); + } + // Will reconsider in future version + // String id = calculateId(iFile); + // if (isResourceInUse(id)) { + // throw new ResourceInUse(iFile.getFullPath().toOSString()); + // } + IDocumentLoader loader = null; + IModelHandler handler = calculateType(iFile); + loader = handler.getDocumentLoader(); + // for this API, "createNew" we assume the IFile does not exist yet + // as checked above, so just create empty document. + IStructuredDocument result = (IStructuredDocument) loader.createNewStructuredDocument(); + return result; + } + + /** + * Factory method, since a proper IStructuredDocument must have a proper + * parser assigned. Note: clients should verify IFile exists before using + * this method. If this IFile does not exist, then + * createNewStructuredDocument is the correct API to use. + * + * @throws ResourceInUse + */ + public IStructuredDocument createStructuredDocumentFor(IFile iFile) throws IOException, CoreException { + if (!iFile.exists()) { + throw new FileNotFoundException(iFile.getFullPath().toOSString()); + } + // Will reconsider in future version + // String id = calculateId(iFile); + // if (isResourceInUse(id)) { + // throw new ResourceInUse(iFile.getFullPath().toOSString()); + // } + IDocumentLoader loader = null; + IModelHandler handler = calculateType(iFile); + loader = handler.getDocumentLoader(); + IStructuredDocument result = (IStructuredDocument) loader.createNewStructuredDocument(iFile); + return result; + } + + /** + * Conveience method, since a proper IStructuredDocument must have a + * proper parser assigned. It should only be used when an empty + * structuredDocument is needed. Otherwise, use IFile form. + * + * @deprecated - TODO: to be removed by C4 do we really need this? I + * recommend to - use createStructuredDocumentFor(filename, + * null, null) - the filename does not need to represent a + * real - file, but can take for form of dummy.jsp, test.xml, + * etc. - That way we don't hard code the handler, but specify + * we - want the handler that "goes with" a certain type of - + * file. + */ + public IStructuredDocument createStructuredDocumentFor(String contentTypeId) { + IDocumentLoader loader = null; + ModelHandlerRegistry cr = getModelHandlerRegistry(); + IModelHandler handler = cr.getHandlerForContentTypeId(contentTypeId); + if (handler == null) + Logger.log(Logger.ERROR, "Program error: no model handler found for " + contentTypeId); //$NON-NLS-1$ + loader = handler.getDocumentLoader(); + IStructuredDocument result = (IStructuredDocument) loader.createNewStructuredDocument(); + return result; + } + + /** + * Conveience method, since a proper IStructuredDocument must have a + * proper parser assigned. + * + * @deprecated -- - TODO: to be removed by C4 I marked as deprecated to + * discouage use of this method. It does not really work for + * JSP fragments, since JSP Fragments need an IFile to + * correctly look up the content settings. Use IFile form + * instead. + */ + public IStructuredDocument createStructuredDocumentFor(String filename, InputStream inputStream, URIResolver resolver) throws IOException { + IDocumentLoader loader = null; + InputStream istream = Utilities.getMarkSupportedStream(inputStream); + if (istream != null) { + istream.reset(); + } + IModelHandler handler = calculateType(filename, istream); + loader = handler.getDocumentLoader(); + IStructuredDocument result = null; + if (inputStream == null) { + result = (IStructuredDocument) loader.createNewStructuredDocument(); + } + else { + result = (IStructuredDocument) loader.createNewStructuredDocument(filename, istream); + } + return result; + } + + /** + * Special case method. This method was created for the special case where + * there is an encoding for input stream that should override all the + * normal rules for encoding. For example, if there is an encoding + * (charset) specified in HTTP response header, then that encoding is used + * to translate the input stream to a string, but then the normal encoding + * rules are ignored, so that the string is not translated twice (for + * example, if its an HTML "file", then even if it contains a charset in + * meta tag, its ignored since its assumed its all correctly decoded by + * the HTTP charset. + */ + public IStructuredDocument createStructuredDocumentFor(String filename, InputStream inputStream, URIResolver resolver, String encoding) throws IOException { + String content = readInputStream(inputStream, encoding); + IStructuredDocument result = createStructuredDocumentFor(filename, content, resolver); + return result; + } + + /** + * Convenience method. This method can be used when the resource does not + * really exist (e.g. when content is being created, but hasn't been + * written to disk yet). Note that since the content is being provided as + * a String, it is assumed to already be decoded correctly so no + * transformation is done. + */ + public IStructuredDocument createStructuredDocumentFor(String filename, String content, URIResolver resolver) throws IOException { + // TODO: avoid all these String instances + StringBuffer contentBuffer = new StringBuffer(content); + IDocumentLoader loader = null; + IModelHandler handler = calculateType(filename, null); + loader = handler.getDocumentLoader(); + IStructuredDocument result = (IStructuredDocument) loader.createNewStructuredDocument(); + StringBuffer convertedContent = loader.handleLineDelimiter(contentBuffer, result); + result.setEncodingMemento(new NullMemento()); + result.setText(this, convertedContent.toString()); + return result; + } + + /** + * @param iFile + * @param result + * @return + * @throws CoreException + */ + private IStructuredModel createUnManagedEmptyModelFor(IFile iFile) throws CoreException { + IStructuredModel result = null; + IModelHandler handler = calculateType(iFile); + String id = calculateId(iFile); + URIResolver resolver = calculateURIResolver(iFile); + + try { + result = _commonCreateModel(id, handler, resolver); + } + catch (ResourceInUse e) { + // impossible, since we're not sharing + // (even if it really is in use ... we don't care) + // this may need to be re-examined. + if (Logger.DEBUG_MODELMANAGER) + Logger.log(Logger.INFO, "ModelMangerImpl::createUnManagedStructuredModelFor. Model unexpectedly in use."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + return result; + } + + /** + * Conveience method. It depends on the loaders newModel method to return + * an appropriate StrucuturedModel appropriately initialized. + */ + public IStructuredModel createUnManagedStructuredModelFor(IFile iFile) throws IOException, CoreException { + IStructuredModel result = null; + result = createUnManagedEmptyModelFor(iFile); + + IDocumentLoader loader = result.getModelHandler().getDocumentLoader(); + IEncodedDocument document = loader.createNewStructuredDocument(iFile); + + result.getStructuredDocument().setText(this, document.get()); + + return result; + } + + /** + * Conveience method. It depends on the loaders newModel method to return + * an appropriate StrucuturedModel appropriately initialized. + */ + public IStructuredModel createUnManagedStructuredModelFor(String contentTypeId) { + return createUnManagedStructuredModelFor(contentTypeId, null); + } + + /** + * Conveience method. It depends on the loaders newModel method to return + * an appropriate StrucuturedModel appropriately initialized. + */ + public IStructuredModel createUnManagedStructuredModelFor(String contentTypeId, URIResolver resolver) { + IStructuredModel result = null; + ModelHandlerRegistry cr = getModelHandlerRegistry(); + IModelHandler handler = cr.getHandlerForContentTypeId(contentTypeId); + try { + result = _commonCreateModel(UNMANAGED_MODEL, handler, resolver); //$NON-NLS-1$ + } + catch (ResourceInUse e) { + // impossible, since we're not sharing + // (even if it really is in use ... we don't care) + // this may need to be re-examined. + if (Logger.DEBUG_MODELMANAGER) + Logger.log(Logger.INFO, "ModelMangerImpl::createUnManagedStructuredModelFor. Model unexpectedly in use."); //$NON-NLS-1$ //$NON-NLS-2$ + } + return result; + } + + private IStructuredModel getExistingModel(Object id) { + IStructuredModel result = null; + + SYNC.acquire(); + /** + * While a good check in theory, it's possible for an event fired to + * cause a listener to access a method that calls this one. + */ + //Assert.isTrue(SYNC.getDepth()==1, "depth not equal to 1"); + // let's see if we already have it in our cache + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + // if not, then we'll simply return null + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + result = sharedObject.theSharedModel; + } else { + SYNC.release(); + } + + return result; + } + + /** + * Note: users of this 'model' must still release it when finished. + * Returns null if there's not a model corresponding to document. + */ + public IStructuredModel getExistingModelForEdit(IDocument document) { + IStructuredModel result = null; + + SYNC.acquire(); + // create a snapshot + Set ids = new HashSet(fManagedObjects.keySet()); + SYNC.release(); + for (Iterator iterator = ids.iterator(); iterator.hasNext();) { + Object potentialId = iterator.next(); + SYNC.acquire(); + if (fManagedObjects.containsKey(potentialId)) { + // check to see if still valid + SYNC.release(); + IStructuredModel tempResult = getExistingModel(potentialId); + if (tempResult!=null && document == tempResult.getStructuredDocument()) { + result = getExistingModelForEdit(potentialId); + break; + } + } else { + SYNC.release(); + } + } + + return result; + } + + /** + * This is similar to the getModel method, except this method does not + * create a model. This method does increment the reference count (if it + * exists). If the model does not already exist in the cache of models, + * null is returned. + */ + public IStructuredModel getExistingModelForEdit(IFile iFile) { + + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + Object id = calculateId(iFile); + IStructuredModel result = getExistingModelForEdit(id); + return result; + } + + /** + * This is similar to the getModel method, except this method does not + * create a model. This method does increment the reference count (if it + * exists). If the model does not already exist in the cache of models, + * null is returned. + * + * @deprecated use IFile form - this one will become protected or private + */ + public IStructuredModel getExistingModelForEdit(Object id) { + + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + IStructuredModel result = null; + boolean doRelease = true; + // let's see if we already have it in our cache + try { + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + // if not, then we'll simply return null + if (sharedObject != null) { + // if shared object is in our cache, then simply increment its ref + // count, + // and return the object. + SYNC.release(); + doRelease=false; + synchronized(sharedObject) { + if (sharedObject.doWait) { + sharedObject.waitForLoadAttempt(); + } + if (sharedObject.theSharedModel!=null) { + _incrCount(sharedObject, EDIT); + } + result = sharedObject.theSharedModel; + } + trace("got existing model for Edit: ", id); //$NON-NLS-1$ + trace(" incremented referenceCountForEdit ", id, sharedObject.referenceCountForEdit); //$NON-NLS-1$ + } + } finally { + if (doRelease) { + SYNC.release(); + } + } + + return result; + } + + /** + * Note: users of this 'model' must still release it when finished. + * Returns null if there's not a model corresponding to document. + */ + public IStructuredModel getExistingModelForRead(IDocument document) { + IStructuredModel result = null; + + SYNC.acquire(); + // create a snapshot + Set ids = new HashSet(fManagedObjects.keySet()); + SYNC.release(); + for (Iterator iterator = ids.iterator(); iterator.hasNext();) { + Object potentialId = iterator.next(); + SYNC.acquire(); + if (fManagedObjects.containsKey(potentialId)) { + // check to see if still valid + SYNC.release(); + IStructuredModel tempResult = getExistingModel(potentialId); + if (tempResult!=null && document == tempResult.getStructuredDocument()) { + result = getExistingModelForRead(potentialId); + break; + } + } else { + SYNC.release(); + } + } + + return result; + } + + public IStructuredModel getExistingModelForRead(IFile iFile) { + + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + Object id = calculateId(iFile); + IStructuredModel result = getExistingModelForRead(id); + return result; + } + + /** + * This is similar to the getModel method, except this method does not + * create a model. This method does increment the reference count (if it + * exists). If the model does not already exist in the cache of models, + * null is returned. + * + * @deprecated use IFile form - this one will become protected or private + */ + public IStructuredModel getExistingModelForRead(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + IStructuredModel result = null; + boolean doRelease = true; + // let's see if we already have it in our cache + try { + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + // if not, then we'll simply return null + if (sharedObject != null) { + // if shared object is in our cache, then simply increment its ref + // count, + // and return the object. + SYNC.release(); + doRelease=false; + + synchronized(sharedObject) { + if (sharedObject.doWait) { + sharedObject.waitForLoadAttempt(); + } + if (sharedObject.theSharedModel!=null) { + _incrCount(sharedObject, READ); + } + result = sharedObject.theSharedModel; + } + } + } finally { + if (doRelease) + SYNC.release(); + } + return result; + } + + /** + * @deprecated DMW: Tom, this is "special" for links builder Assuming its + * still needed, wouldn't it be better to change to + * getExistingModels()? -- will be removed. Its not thread + * safe for one thread to get the Enumeration, when underlying + * data could be changed in another thread. + */ + public Enumeration getExistingModelIds() { + try { + SYNC.acquire(); + // create a copy + Vector keys = new Vector( fManagedObjects.keySet() ); + return keys.elements(); + } finally { + SYNC.release(); + } + } + + // TODO: replace (or supplement) this is a "model info" association to the + // IFile that created the model + private IFile getFileFor(IStructuredModel model) { + if (model == null) + return null; + String path = model.getBaseLocation(); + if (path == null || path.length() == 0) { + Object id = model.getId(); + if (id == null) + return null; + path = id.toString(); + } + // TOODO needs rework for linked resources + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IFile file = root.getFileForLocation(new Path(path)); + return file; + } + + /** + * One of the primary forms to get a managed model + */ + public IStructuredModel getModelForEdit(IFile iFile) throws IOException, CoreException { + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + return _commonGetModel(iFile, EDIT, null, null); + } + + public IStructuredModel getModelForEdit(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + return _commonGetModel(iFile, EDIT, encodingRule); + } + + public IStructuredModel getModelForEdit(IFile iFile, String encoding, String lineDelimiter) throws java.io.UnsupportedEncodingException, IOException, CoreException { + + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + return _commonGetModel(iFile, EDIT, encoding, lineDelimiter); + } + + public IStructuredModel getModelForEdit(IStructuredDocument document) { + return _getModelFor(document, EDIT); + } + + /** + * @see IModelManager + * @deprecated use IFile or String form + */ + public IStructuredModel getModelForEdit(Object id, InputStream inputStream, URIResolver resolver) throws java.io.UnsupportedEncodingException, IOException { + + Assert.isNotNull(id, "requested model id can not be null"); //$NON-NLS-1$ + String stringId = id.toString(); + return getModelForEdit(stringId, Utilities.getMarkSupportedStream(inputStream), resolver); + } + + /** + * @see IModelManager + * @deprecated - use IFile or String form + */ + public IStructuredModel getModelForEdit(Object id, Object modelType, String encodingName, String lineDelimiter, InputStream inputStream, URIResolver resolver) throws java.io.UnsupportedEncodingException, IOException { + + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + String stringId = id.toString(); + return getModelForEdit(stringId, Utilities.getMarkSupportedStream(inputStream), resolver); + } + + public IStructuredModel getModelForEdit(String id, InputStream inputStream, URIResolver resolver) throws IOException { + if (id == null) { + throw new IllegalArgumentException("Program Error: id may not be null"); //$NON-NLS-1$ + } + IStructuredModel result = null; + + InputStream istream = Utilities.getMarkSupportedStream(inputStream); + IModelHandler handler = calculateType(id, istream); + if (handler != null) { + result = _commonCreateModel(istream, id, handler, resolver, EDIT, null, null); + } + else { + Logger.log(Logger.INFO, "no model handler found for id"); //$NON-NLS-1$ + } + return result; + } + + /** + * One of the primary forms to get a managed model + */ + public IStructuredModel getModelForRead(IFile iFile) throws IOException, CoreException { + + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + return _commonGetModel(iFile, READ, null, null); + } + + public IStructuredModel getModelForRead(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + return _commonGetModel(iFile, READ, encodingRule); + } + + public IStructuredModel getModelForRead(IFile iFile, String encodingName, String lineDelimiter) throws java.io.UnsupportedEncodingException, IOException, CoreException { + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + return _commonGetModel(iFile, READ, encodingName, lineDelimiter); + } + + public IStructuredModel getModelForRead(IStructuredDocument document) { + return _getModelFor(document, READ); + } + + /** + * @see IModelManager + * @deprecated use IFile or String form + */ + public IStructuredModel getModelForRead(Object id, InputStream inputStream, URIResolver resolver) throws java.io.UnsupportedEncodingException, IOException { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + String stringId = id.toString(); + return getModelForRead(stringId, Utilities.getMarkSupportedStream(inputStream), resolver); + } + + /** + * @see IModelManager + * @deprecated use IFile form + */ + public IStructuredModel getModelForRead(Object id, Object modelType, String encodingName, String lineDelimiter, InputStream inputStream, URIResolver resolver) throws java.io.UnsupportedEncodingException, IOException { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + String stringId = id.toString(); + return getModelForRead(stringId, Utilities.getMarkSupportedStream(inputStream), resolver); + } + + public IStructuredModel getModelForRead(String id, InputStream inputStream, URIResolver resolver) throws IOException { + InputStream istream = Utilities.getMarkSupportedStream(inputStream); + IModelHandler handler = calculateType(id, istream); + IStructuredModel result = null; + result = _commonCreateModel(istream, id, handler, resolver, READ, null, null); + return result; + } + + /** + * @deprecated - only temporarily visible + */ + public ModelHandlerRegistry getModelHandlerRegistry() { + if (fModelHandlerRegistry == null) { + fModelHandlerRegistry = ModelHandlerRegistry.getInstance(); + } + return fModelHandlerRegistry; + } + + /** + * @see IModelManager#getNewModelForEdit(IFile, boolean) + */ + public IStructuredModel getNewModelForEdit(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException { + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + SharedObject sharedObject = _commonNewModel(iFile, force); + synchronized(sharedObject) { + sharedObject.referenceCountForEdit = 1; + } + sharedObject.setLoaded(); + return sharedObject.theSharedModel; + } + + /** + * @see IModelManager#getNewModelForRead(IFile, boolean) + */ + public IStructuredModel getNewModelForRead(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException { + + Assert.isNotNull(iFile, "IFile parameter can not be null"); //$NON-NLS-1$ + SharedObject sharedObject = _commonNewModel(iFile, force); + SYNC.acquire(); + synchronized(sharedObject) { + if (sharedObject.theSharedModel!=null) { + sharedObject.referenceCountForRead = 1; + } + } + SYNC.release(); + sharedObject.setLoaded(); + return sharedObject.theSharedModel; + } + + /** + * This function returns the reference count of underlying model. + * + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + */ + public int getReferenceCount(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + int count = 0; + + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized (sharedObject) { + count = sharedObject.referenceCountForRead + sharedObject.referenceCountForEdit; + } + } else { + SYNC.release(); + } + return count; + } + + /** + * This function returns the reference count of underlying model. + * + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + */ + public int getReferenceCountForEdit(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + int count = 0; + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized(sharedObject) { + count = sharedObject.referenceCountForEdit; + } + } else { + SYNC.release(); + } + return count; + } + + /** + * This function returns the reference count of underlying model. + * + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + */ + public int getReferenceCountForRead(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + int count = 0; + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized(sharedObject) { + count = sharedObject.referenceCountForRead; + } + } else { + SYNC.release(); + } + return count; + } + + private void handleConvertLineDelimiters(IStructuredDocument structuredDocument, IFile iFile, EncodingRule encodingRule, EncodingMemento encodingMemento) throws CoreException, MalformedOutputExceptionWithDetail, UnsupportedEncodingException { + if (structuredDocument.getNumberOfLines() > 1) { + convertLineDelimiters(structuredDocument, iFile); + } + } + + private void handleProgramError(Throwable t) { + + Logger.logException("Impossible Program Error", t); //$NON-NLS-1$ + } + + /** + * This function returns true if there are other references to the + * underlying model. + */ + public boolean isShared(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + int count = 0; + boolean result = false; + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized(sharedObject) { + count = sharedObject.referenceCountForRead + sharedObject.referenceCountForEdit; + } + } else { + SYNC.release(); + } + result = count > 1; + return result; + } + + /** + * This function returns true if there are other references to the + * underlying model. + * + * @param id + * Object The id of the model + */ + public boolean isSharedForEdit(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + int count = 0; + boolean result = false; + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized(sharedObject) { + count = sharedObject.referenceCountForEdit; + } + } else { + SYNC.release(); + } + result = count > 1; + return result; + } + + /** + * This function returns true if there are other references to the + * underlying model. + * + * @param id + * Object The id of the model + */ + public boolean isSharedForRead(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + int count = 0; + boolean result = false; + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject != null) { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized(sharedObject) { + count = sharedObject.referenceCountForRead; + } + } else { + SYNC.release(); + } + result = count > 1; + return result; + } + + /** + * This method can be called to determine if the model manager is within a + * "aboutToChange" and "changed" sequence. + * + * @deprecated the manager does not otherwise interact with these states + * @return false + */ + public boolean isStateChanging() { + // doesn't seem to be used anymore + return false; + } + + /** + * This method changes the id of the model. TODO: try to refine the design + * not to use this function + */ + public void moveModel(Object oldId, Object newId) { + Assert.isNotNull(oldId, "old id parameter can not be null"); //$NON-NLS-1$ + Assert.isNotNull(newId, "new id parameter can not be null"); //$NON-NLS-1$ + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(oldId); + // if not found in cache, ignore request. + // this would normally be a program error + if (sharedObject != null) { + fManagedObjects.remove(oldId); + fManagedObjects.put(newId, sharedObject); + } + SYNC.release(); + } + + private String readInputStream(InputStream inputStream, String ianaEncodingName) throws UnsupportedEncodingException, IOException { + + String allText = null; + if ((ianaEncodingName != null) && (ianaEncodingName.length() != 0)) { + String enc = CodedIO.getAppropriateJavaCharset(ianaEncodingName); + if (enc == null) { + // if no conversion was possible, let's assume that + // the encoding is already a java encoding name, so we'll + // proceed with that assumption. This is the case, for + // example, + // for the reload() procedure. + // If in fact it is not a valid java encoding, then + // the "allText=" line will cause an + // UnsupportedEncodingException + enc = ianaEncodingName; + } + allText = readInputStream(new InputStreamReader(inputStream, enc)); + } + else { + // we normally assume encoding is provided for this method, but if + // not, + // we'll use platform default + allText = readInputStream(new InputStreamReader(inputStream)); + } + return allText; + } + + private String readInputStream(InputStreamReader inputStream) throws IOException { + + int numRead = 0; + StringBuffer buffer = new StringBuffer(); + char tBuff[] = new char[READ_BUFFER_SIZE]; + while ((numRead = inputStream.read(tBuff, 0, tBuff.length)) != -1) { + buffer.append(tBuff, 0, numRead); + } + // remember -- we didn't open stream ... so we don't close it + return buffer.toString(); + } + + /* + * @see IModelManager#reinitialize(IStructuredModel) + */ + public IStructuredModel reinitialize(IStructuredModel model) { + + // getHandler (assume its the "new one") + IModelHandler handler = model.getModelHandler(); + // getLoader for that new one + IModelLoader loader = handler.getModelLoader(); + // ask it to reinitialize + model = loader.reinitialize(model); + // the loader should check to see if the one it received + // is the same type it would normally create. + // if not, it must "start from scratch" and create a whole + // new one. + // if it is of the same type, it should just 'replace text' + // replacing all the existing text with the new text. + // the important one is the JSP loader ... it should go through + // its embedded content checking and initialization + return model; + } + + void releaseFromEdit(IStructuredModel structuredModel) { + Object id = structuredModel.getId(); + if (id.equals(UNMANAGED_MODEL) || id.equals(DUPLICATED_MODEL)) { + cleanupDiscardedModel(structuredModel); + } + else { + releaseFromEdit(id); + } + + } + + void releaseFromRead(IStructuredModel structuredModel) { + Object id = structuredModel.getId(); + if (id.equals(UNMANAGED_MODEL) || id.equals(DUPLICATED_MODEL)) { + cleanupDiscardedModel(structuredModel); + } + else { + releaseFromRead(id); + } + + } + /** + * default for use in same package, not subclasses + * + */ + private void releaseFromEdit(Object id) { + // ISSUE: many of these asserts should be changed to "logs" + // and continue to limp along? + + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + SharedObject sharedObject = null; + + // ISSUE: here we need better "spec" what to do with + // unmanaged or duplicated models. Release still needs + // to be called on them, for now, but the model manager + // doesn't need to do anything. + if (id.equals(UNMANAGED_MODEL) || id.equals(DUPLICATED_MODEL)) { + throw new IllegalArgumentException("Ids of UNMANAGED_MODEL or DUPLICATED_MODEL are illegal here"); + } + else { + SYNC.acquire(); + sharedObject = (SharedObject) fManagedObjects.get(id); + SYNC.release(); + + Assert.isNotNull(sharedObject, "release was requested on a model that was not being managed"); //$NON-NLS-1$ + sharedObject.waitForLoadAttempt(); + SYNC.acquire(); + synchronized(sharedObject) { + _decrCount(sharedObject, EDIT); + if ((sharedObject.referenceCountForRead == 0) && (sharedObject.referenceCountForEdit == 0)) { + discardModel(id, sharedObject); + } + } + SYNC.release(); + // if edit goes to zero, but still open for read, + // then we should reload here, so we are in synch with + // contents on disk. + // ISSUE: should we check isDirty here? + // ANSWER: here, for now now. model still has its own dirty + // flag for some reason. + // we need to address * that * too. + + synchronized(sharedObject) { + if ((sharedObject.referenceCountForRead > 0) && (sharedObject.referenceCountForEdit == 0) && sharedObject.theSharedModel.isDirty()) { + signalPreLifeCycleListenerRevert(sharedObject.theSharedModel); + revertModel(id, sharedObject); + /* + * Because model events are fired to notify about the + * revert's changes, and listeners can still get/release + * the model from this thread (locking prevents it being + * done from other threads), the reference counts could + * have changed since we entered this if block, and the + * model could have been discarded. Check the counts again. + */ + if (sharedObject.referenceCountForRead > 0 && sharedObject.referenceCountForEdit == 0) { + sharedObject.theSharedModel.setDirtyState(false); + } + signalPostLifeCycleListenerRevert(sharedObject.theSharedModel); + } + } + + } + } + + // private for now, though public forms have been requested, in past. + private void revertModel(Object id, SharedObject sharedObject) { + IStructuredDocument structuredDocument = sharedObject.theSharedModel.getStructuredDocument(); + FileBufferModelManager.getInstance().revert(structuredDocument); + } + + private void signalPreLifeCycleListenerRevert(IStructuredModel structuredModel) { + int type = ModelLifecycleEvent.MODEL_REVERT | ModelLifecycleEvent.PRE_EVENT; + // what's wrong with this design that a cast is needed here!? + ModelLifecycleEvent event = new ModelLifecycleEvent(structuredModel, type); + ((AbstractStructuredModel) structuredModel).signalLifecycleEvent(event); + } + + private void signalPostLifeCycleListenerRevert(IStructuredModel structuredModel) { + int type = ModelLifecycleEvent.MODEL_REVERT | ModelLifecycleEvent.POST_EVENT; + // what's wrong with this design that a cast is needed here!? + ModelLifecycleEvent event = new ModelLifecycleEvent(structuredModel, type); + ((AbstractStructuredModel) structuredModel).signalLifecycleEvent(event); + } + + private void discardModel(Object id, SharedObject sharedObject) { + SYNC.acquire(); + fManagedObjects.remove(id); + SYNC.release(); + IStructuredDocument structuredDocument = sharedObject.theSharedModel.getStructuredDocument(); + + if (structuredDocument == null) { + Platform.getLog(SSECorePlugin.getDefault().getBundle()).log(new Status(IStatus.ERROR, SSECorePlugin.ID, IStatus.ERROR, "Attempted to discard a structured model but the underlying document has already been set to null: " + sharedObject.theSharedModel.getBaseLocation(), null)); + } + + cleanupDiscardedModel(sharedObject.theSharedModel); + } + + private void cleanupDiscardedModel(IStructuredModel structuredModel) { + IStructuredDocument structuredDocument = structuredModel.getStructuredDocument(); + /* + * This call (and setting the StructuredDocument to null) were + * previously done within the model itself, but for concurrency it + * must be done here during a synchronized release. + */ + structuredModel.getFactoryRegistry().release(); + + /* + * For structured documents originating from file buffers, disconnect + * us from the file buffer, now. + */ + FileBufferModelManager.getInstance().releaseModel(structuredDocument); + + /* + * Setting the document to null is required since some subclasses of + * model might have "cleanup" of listeners, etc., to remove, which + * were initialized during the initial setStructuredDocument. + * + * The model itself in particular may have internal listeners used to + * coordinate the document with its own "structure". + */ + structuredModel.setStructuredDocument(null); + } + + + /** + * default for use in same package, not subclasses + * + */ + private void releaseFromRead(Object id) { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + SharedObject sharedObject = null; + + if (id.equals(UNMANAGED_MODEL) || id.equals(DUPLICATED_MODEL)) { + throw new IllegalArgumentException("Ids of UNMANAGED_MODEL or DUPLICATED_MODEL are illegal here"); + } + else { + SYNC.acquire(); + sharedObject = (SharedObject) fManagedObjects.get(id); + SYNC.release(); + Assert.isNotNull(sharedObject, "release was requested on a model that was not being managed"); //$NON-NLS-1$ + sharedObject.waitForLoadAttempt(); + } + SYNC.acquire(); + synchronized(sharedObject) { + _decrCount(sharedObject, READ); + if ((sharedObject.referenceCountForRead == 0) && (sharedObject.referenceCountForEdit == 0)) { + discardModel(id, sharedObject); + } + } + SYNC.release(); + } + + /** + * This is similar to the getModel method, except this method does not use + * the cached version, but forces the cached version to be replaced with a + * fresh, unchanged version. Note: this method does not change any + * reference counts. Also, if there is not already a cached version of the + * model, then this call is essentially ignored (that is, it does not put + * a model in the cache) and returns null. + * + * @deprecated - will become protected, use reload directly on model + */ + public IStructuredModel reloadModel(Object id, java.io.InputStream inputStream) throws java.io.UnsupportedEncodingException { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + + // get the existing model associated with this id + IStructuredModel structuredModel = getExistingModel(id); + // for the model to be null is probably an error (that is, + // reload should not have been called, but we'll guard against + // a null pointer example and return null if we are no longer managing + // that model. + if (structuredModel != null) { + // get loader based on existing type + // dmwTODO evaluate when reload should occur + // with potentially new type (e.g. html 'save as' jsp). + IModelHandler handler = structuredModel.getModelHandler(); + IModelLoader loader = handler.getModelLoader(); + // ask the loader to re-load + loader.reload(Utilities.getMarkSupportedStream(inputStream), structuredModel); + trace("re-loading model", id); //$NON-NLS-1$ + } + return structuredModel; + } + + public void saveModel(IFile iFile, String id, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + Assert.isNotNull(iFile, "file parameter can not be null"); //$NON-NLS-1$ + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + + // let's see if we already have it in our cache + + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject == null || sharedObject.theSharedModel == null) { + SYNC.release(); + throw new IllegalStateException(SSECoreMessages.Program_Error__ModelManage_EXC_); //$NON-NLS-1$ = "Program Error: ModelManagerImpl::saveModel. Model should be in the cache" + } + else { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + + /** + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=221610 + * + * Sync removed from here to prevent deadlock. Although the model + * instance may disappear or be made invalid while the save is + * happening, the document itself still has the contents we're + * trying to save. Simultaneous saves should be throttled by + * resource locking without our intervention. + */ + boolean saved = false; + // if this model was based on a File Buffer and we're writing back + // to the same location, use the buffer to do the writing + if (FileBufferModelManager.getInstance().isExistingBuffer(sharedObject.theSharedModel.getStructuredDocument())) { + ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(sharedObject.theSharedModel.getStructuredDocument()); + IPath fileLocation = FileBuffers.normalizeLocation(iFile.getFullPath()); + if (fileLocation.equals(buffer.getLocation())) { + buffer.commit(new NullProgressMonitor(), true); + saved = true; + } + } + if (!saved) { + IStructuredModel model = sharedObject.theSharedModel; + IStructuredDocument document = model.getStructuredDocument(); + saveStructuredDocument(document, iFile, encodingRule); + trace("saving model", id); //$NON-NLS-1$ + } + sharedObject.theSharedModel.setDirtyState(false); + sharedObject.theSharedModel.setNewState(false); + } + } + + /** + * Saving the model really just means to save it's structured document. + * + * @param id + * @param outputStream + * @param encodingRule + * @throws UnsupportedEncodingException + * @throws IOException + * @throws CoreException + */ + public void saveModel(String id, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + + // let's see if we already have it in our cache + + SYNC.acquire(); + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject == null) { + SYNC.release(); + throw new IllegalStateException(SSECoreMessages.Program_Error__ModelManage_EXC_); //$NON-NLS-1$ = "Program Error: ModelManagerImpl::saveModel. Model should be in the cache" + } + else { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + /** + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=221610 + * + * Sync removed from here to prevent deadlock. Although the model + * instance may disappear or be made invalid while the save is + * happening, the document itself still has the contents we're + * trying to save. Simultaneous saves should be throttled by + * resource locking without our intervention. + */ + /* + * if this model was based on a File Buffer and we're writing back + * to the same location, use the buffer to do the writing + */ + if (FileBufferModelManager.getInstance().isExistingBuffer(sharedObject.theSharedModel.getStructuredDocument())) { + ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(sharedObject.theSharedModel.getStructuredDocument()); + buffer.commit(new NullProgressMonitor(), true); + } + else { + IFile iFile = getFileFor(sharedObject.theSharedModel); + IStructuredModel model = sharedObject.theSharedModel; + IStructuredDocument document = model.getStructuredDocument(); + saveStructuredDocument(document, iFile); + trace("saving model", id); //$NON-NLS-1$ + } + sharedObject.theSharedModel.setDirtyState(false); + sharedObject.theSharedModel.setNewState(false); + } + } + + /** + * @deprecated - this method is less efficient than IFile form, since it + * requires an extra "copy" of byte array, and should be avoid + * in favor of the IFile form. + */ + public void saveModel(String id, OutputStream outputStream, EncodingRule encodingRule) throws UnsupportedEncodingException, CoreException, IOException { + Assert.isNotNull(id, "id parameter can not be null"); //$NON-NLS-1$ + + SYNC.acquire(); + // let's see if we already have it in our cache + SharedObject sharedObject = (SharedObject) fManagedObjects.get(id); + if (sharedObject == null) { + SYNC.release(); + throw new IllegalStateException(SSECoreMessages.Program_Error__ModelManage_EXC_); //$NON-NLS-1$ = "Program Error: ModelManagerImpl::saveModel. Model should be in the cache" + } + else { + SYNC.release(); + sharedObject.waitForLoadAttempt(); + synchronized(sharedObject) { + CodedStreamCreator codedStreamCreator = new CodedStreamCreator(); + codedStreamCreator.set(sharedObject.theSharedModel.getId(), new DocumentReader(sharedObject.theSharedModel.getStructuredDocument())); + codedStreamCreator.setPreviousEncodingMemento(sharedObject.theSharedModel.getStructuredDocument().getEncodingMemento()); + ByteArrayOutputStream byteArrayOutputStream = codedStreamCreator.getCodedByteArrayOutputStream(encodingRule); + byte[] outputBytes = byteArrayOutputStream.toByteArray(); + outputStream.write(outputBytes); + trace("saving model", id); //$NON-NLS-1$ + sharedObject.theSharedModel.setDirtyState(false); + sharedObject.theSharedModel.setNewState(false); + } + } + } + + public void saveStructuredDocument(IStructuredDocument structuredDocument, IFile iFile) throws UnsupportedEncodingException, CoreException, IOException { + saveStructuredDocument(structuredDocument, iFile, EncodingRule.CONTENT_BASED); + } + + public void saveStructuredDocument(IStructuredDocument structuredDocument, IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, CoreException, IOException { + Assert.isNotNull(iFile, "file parameter can not be null"); //$NON-NLS-1$ + if (FileBufferModelManager.getInstance().isExistingBuffer(structuredDocument)) { + ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(structuredDocument); + if (buffer.getLocation().equals(iFile.getFullPath()) || buffer.getLocation().equals(iFile.getLocation())) { + buffer.commit(new NullProgressMonitor(), true); + } + } + else { + // IModelHandler handler = calculateType(iFile); + // IDocumentDumper dumper = handler.getDocumentDumper(); + CodedStreamCreator codedStreamCreator = new CodedStreamCreator(); + Reader reader = new DocumentReader(structuredDocument); + codedStreamCreator.set(iFile, reader); + codedStreamCreator.setPreviousEncodingMemento(structuredDocument.getEncodingMemento()); + EncodingMemento encodingMemento = codedStreamCreator.getCurrentEncodingMemento(); + + // be sure document's is updated, in case exception is thrown in + // getCodedByteArrayOutputStream + structuredDocument.setEncodingMemento(encodingMemento); + + // Convert line delimiters after encoding memento is figured out, + // but + // before writing to output stream. + handleConvertLineDelimiters(structuredDocument, iFile, encodingRule, encodingMemento); + + ByteArrayOutputStream codedByteStream = codedStreamCreator.getCodedByteArrayOutputStream(encodingRule); + InputStream codedStream = new ByteArrayInputStream(codedByteStream.toByteArray()); + if (iFile.exists()) + iFile.setContents(codedStream, true, true, null); + else + iFile.create(codedStream, false, null); + codedByteStream.close(); + codedStream.close(); + } + } + + /** + * Common trace method + */ + private void trace(String msg, Object id) { + if (Logger.DEBUG_MODELMANAGER) { + Logger.log(Logger.INFO, msg + " " + Utilities.makeShortId(id)); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Common trace method + */ + private void trace(String msg, Object id, int value) { + if (Logger.DEBUG_MODELMANAGER) { + Logger.log(Logger.INFO, msg + Utilities.makeShortId(id) + " (" + value + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + boolean isIdInUse(String newId) { + boolean inUse = false; + SYNC.acquire(); + SharedObject object =(SharedObject) fManagedObjects.get(newId); + if (object!=null) { + inUse = object.theSharedModel!=null; + } + SYNC.release(); + return inUse; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/PrefUtil.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/PrefUtil.java new file mode 100644 index 0000000..ee29ad3 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/model/PrefUtil.java
@@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010 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.wst.sse.core.internal.model; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.ConfigurationScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; +import org.eclipse.core.runtime.preferences.IPreferencesService; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.osgi.util.NLS; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.osgi.service.prefs.Preferences; + +class PrefUtil { + + static long WAIT_INTERVAL_MS = 500; + static int WAIT_DELAY = getInt("org.eclipse.wst.sse.core.modelmanager.maxWaitDuringConcurrentLoad"); + static boolean ALLOW_INTERRUPT_WAITING_THREAD = getBoolean("org.eclipse.wst.sse.core.modelmanager.allowInterruptsDuringConcurrentLoad"); + + /** Base of millisecond timings, to avoid wrapping */ + private static final long MILLI_ORIGIN = System.currentTimeMillis(); + + /** + * Returns millisecond time offset by origin + */ + static final long now() { + return System.currentTimeMillis() - MILLI_ORIGIN; + } + + private static IEclipsePreferences.IPreferenceChangeListener LISTENER; + static { + InstanceScope scope = new InstanceScope(); + IEclipsePreferences instancePrefs = scope.getNode(SSECorePlugin.ID); + LISTENER = new IEclipsePreferences.IPreferenceChangeListener() { + + public void preferenceChange(PreferenceChangeEvent event) { + + if ("modelmanager.maxWaitDuringConcurrentLoad".equals(event.getKey())) { + WAIT_DELAY = getInt("org.eclipse.wst.sse.core.modelmanager.maxWaitDuringConcurrentLoad"); + } + else if ("modelmanager.allowInterruptsDuringConcurrentLoad".equals(event.getKey())) { + ALLOW_INTERRUPT_WAITING_THREAD = getBoolean("org.eclipse.wst.sse.core.modelmanager.allowInterruptsDuringConcurrentLoad"); + } + } + }; + instancePrefs.addPreferenceChangeListener(LISTENER); + } + + private static String getProperty(String property) { + // Importance order is: + // default-default < instanceScope < configurationScope < systemProperty + // < envVar + String value = null; + + if (value == null) { + value = System.getenv(property); + } + if (value == null) { + value = System.getProperty(property); + } + if (value == null) { + IPreferencesService preferencesService = Platform.getPreferencesService(); + + String key = property; + if (property != null && property.startsWith(SSECorePlugin.ID)) { + // +1, include the "." + key = property.substring(SSECorePlugin.ID.length() + 1, property.length()); + } + InstanceScope instance = new InstanceScope(); + ConfigurationScope config = new ConfigurationScope(); + + Preferences instanceNode = instance.getNode(SSECorePlugin.ID); + Preferences configNode = config.getNode(SSECorePlugin.ID); + value = preferencesService.get(key, getDefault(property), new Preferences[]{configNode,instanceNode}); + } + + return value; + } + + private static String getDefault(String property) { + // this is the "default-default" + if ("org.eclipse.wst.sse.core.modelmanager.maxWaitDuringConcurrentLoad".equals(property)) { + return "0"; + } + else if ("org.eclipse.wst.sse.core.modelmanager.allowInterruptsDuringConcurrentLoad".equals(property)) { + return "false"; + } + return null; + } + + private static boolean getBoolean(String key) { + String property = getProperty(key); + // if (property != null) { + // System.out.println("Tweak: " + key + "=" + Boolean.parseBoolean(property)); //$NON-NLS-1$ //$NON-NLS-2$ + // } + return (property != null ? Boolean.valueOf(property) : Boolean.valueOf(getDefault(key))) + .booleanValue(); + } + + private static int getInt(String key) { + String property = getProperty(key); + int size = 0; + if (property != null) { + try { + size = Integer.parseInt(property); + // System.out.println("Tweak: " + key + "=" + size); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (NumberFormatException e) { + size = getDefaultInt(key, property, size); + } + } + else { + size = getDefaultInt(key, property, size); + } + return size; + } + + private static int getDefaultInt(String key, String property, int size) { + // ignored + try { + size = Integer.parseInt(getDefault(key)); + } + catch (NumberFormatException e1) { + handleIntParseException(key, property, e1); + size = 0; + } + return size; + } + + private static void handleIntParseException(String key, String property, NumberFormatException e1) { + Exception n = new Exception(NLS.bind( + "Exception during parse of default value for key ''{0}'' value was ''{1}''. Using 0 instead", //$NON-NLS-1$ + key, property), e1); + n.printStackTrace(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistry.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistry.java new file mode 100644 index 0000000..60a10a8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistry.java
@@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.EmbeddedTypeHandler; + +/** + * The entries in this registry are, conceptually, singleton's Since only one + * instance is created in the registry, and then that instance returned when + * required. + * + * Note that there is intentionally no 'remove' method, Since the registry + * itself is read it when once, from the platform's plugin registry, and is + * not intended to be modified after that. A change in an extenstion in a + * plugin.xml will only take effect when the workbench is re-started. + * + */ +public interface EmbeddedTypeRegistry { + + /** + * Method to return the specific type for the specific mimetype. + */ + public EmbeddedTypeHandler getTypeFor(String mimeType); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistryImpl.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistryImpl.java new file mode 100644 index 0000000..8aa890f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistryImpl.java
@@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import java.util.HashSet; +import java.util.Iterator; + +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.EmbeddedTypeHandler; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IDocumentTypeHandler; + + +/** + * + */ +public class EmbeddedTypeRegistryImpl implements EmbeddedTypeRegistry { + + private static EmbeddedTypeRegistry instance = null; + + public synchronized static EmbeddedTypeRegistry getInstance() { + if (instance == null) { + instance = new EmbeddedTypeRegistryImpl(); + } + return instance; + } + + private HashSet hashSet = null; + private EmbeddedTypeHandler registryDefaultHandler = null; + + /* + * @see ContentTypeRegistry#getTypeFor(String) + */ + /** + * Constructor for ContentTypeRegistryImpl. + */ + private EmbeddedTypeRegistryImpl() { + super(); + hashSet = new HashSet(); + new EmbeddedTypeRegistryReader().readRegistry(hashSet); + } + + /** + * @see ContentTypeRegistry#add(ContentTypeDescription) + */ + void add(IDocumentTypeHandler contentTypeDescription) { + hashSet.add(contentTypeDescription); + } + + private EmbeddedTypeHandler getJSPDefaultEmbeddedType() { + return getTypeFor("text/html"); //$NON-NLS-1$ + } + + /** + * Method getRegistryDefault. We cache the default handler, since can't + * change once plugin descriptors are loaded. + * + * @return EmbeddedTypeHandler + */ + private EmbeddedTypeHandler getRegistryDefault() { + if (registryDefaultHandler == null) { + Iterator it = hashSet.iterator(); + while ((registryDefaultHandler == null) && (it.hasNext())) { // safe + // cast + // since + // 'add' + // requires + // EmbeddedContentTypeDescription + EmbeddedTypeHandler item = (EmbeddedTypeHandler) it.next(); + if ((item != null) && (item.isDefault())) { + registryDefaultHandler = item; + break; + } + } + } + return registryDefaultHandler; + } + + /** + * Finds the contentTypeDescription based on literal id. Its basically a + * "first found first returned". Note the order is fairly unpredictable, + * so non-unique ids would cause problems. + */ + public EmbeddedTypeHandler getTypeFor(String mimeType) { + // Note: the reason we have this precondition is that the + // default is different inside the registry than when called, + // for example, from the JSPLoader. For the JSPLoader, if there + // is no mimetype, the default should be HTML. Here, if there is + // some mimetype, but it is not recognized, we return a default + // for XML. This is required for various voice xml types, etc. + EmbeddedTypeHandler found = null; + if (mimeType == null || mimeType.trim().length() == 0) { + found = getJSPDefaultEmbeddedType(); + } else { + Iterator it = hashSet.iterator(); + while ((found == null) && (it.hasNext())) { // safe cast since + // 'add' requires + // EmbeddedContentTypeDescription + EmbeddedTypeHandler item = (EmbeddedTypeHandler) it.next(); + if ((item != null) && (item.getSupportedMimeTypes().contains(mimeType))) { + found = item; + break; + } + } + // if no exact match, do the "looser" check + if(found == null) { + it = hashSet.iterator(); + while ((found == null) && (it.hasNext())) { + EmbeddedTypeHandler item = (EmbeddedTypeHandler) it.next(); + if ((item != null) && (item.canHandleMimeType(mimeType))) { + found = item; + break; + } + } + } + } + // no matches, use default + if (found == null) { + found = getRegistryDefault(); + } + return found; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistryReader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistryReader.java new file mode 100644 index 0000000..698f7a1 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/EmbeddedTypeRegistryReader.java
@@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import java.util.Set; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.EmbeddedTypeHandler; +import org.eclipse.wst.sse.core.internal.util.Assert; + + +public class EmbeddedTypeRegistryReader { + protected String ATT_CLASS = "class"; //$NON-NLS-1$ + protected String EXTENSION_POINT_ID = "embeddedTypeHandler"; //$NON-NLS-1$ + + + protected String PLUGIN_ID = "org.eclipse.wst.sse.core"; //$NON-NLS-1$ + protected String TAG_NAME = "embeddedTypeHandler"; //$NON-NLS-1$ + + EmbeddedTypeRegistryReader() { + super(); + } + + protected EmbeddedTypeHandler readElement(IConfigurationElement element) { + + EmbeddedTypeHandler contentTypeDescription = null; + if (element.getName().equals(TAG_NAME)) { + try { + contentTypeDescription = (EmbeddedTypeHandler) element.createExecutableExtension(ATT_CLASS); + } catch (Exception e) { + Logger.logException(e); + } + } + Assert.isNotNull(contentTypeDescription, "Error reading content type description"); //$NON-NLS-1$ + return contentTypeDescription; + } + + /** + * We simply require an 'add' method, of what ever it is we are to read + * into + */ + void readRegistry(Set set) { + IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); + IExtensionPoint point = extensionRegistry.getExtensionPoint(PLUGIN_ID, EXTENSION_POINT_ID); + if (point != null) { + IConfigurationElement[] elements = point.getConfigurationElements(); + for (int i = 0; i < elements.length; i++) { + EmbeddedTypeHandler embeddedContentType = readElement(elements[i]); + // null can be returned if there's an error reading the + // element + if (embeddedContentType != null) { + set.add(embeddedContentType); + } + } + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerRegistry.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerRegistry.java new file mode 100644 index 0000000..72bfa13 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerRegistry.java
@@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.util.Utilities; + + +public class ModelHandlerRegistry { + private static ModelHandlerRegistry instance = null; + static final String INTERNAL_DEFAULT_EXTENSION = "org.eclipse.wst.xml.core.internal.modelhandler"; //$NON-NLS-1$ + + public synchronized static ModelHandlerRegistry getInstance() { + if (instance == null) { + instance = new ModelHandlerRegistry(); + } + return instance; + } + + private IModelHandler defaultHandler = null; + private ModelHandlerRegistryReader reader = new ModelHandlerRegistryReader(); + + private ModelHandlerRegistry() { + super(); + reader = new ModelHandlerRegistryReader().readRegistry(); + } + + /** + * Finds the default model handler. Note: we still go through the registry + * to be sure to get the existing instance, but then we do remember it, so + * subsequent requests will be faster. The first time through, we do check + * the whole list, to be sure there is only one. + * + */ + final public IModelHandler getDefault() { + if (defaultHandler == null) { + IConfigurationElement[] elements = reader.elements; + for (int i = 0; i < elements.length; i++) { + boolean ofInterest = reader.isElementDefault(elements[i]); + if (ofInterest) { + /* + * If, here within the search loop we've already found one + * defaultHandler, then something is wrong! + */ + if (defaultHandler == null) { + defaultHandler = reader.getInstance(elements[i]); + } + else { + String errorString = "Program or configuration error. More than one default content handler found"; //$NON-NLS-1$ + Logger.log(Logger.ERROR, errorString); + throw new IllegalStateException(errorString); + } + } + } + } + if (defaultHandler == null) { + String errorString = "Program or configuration error. No default content type handler found."; //$NON-NLS-1$ + Logger.log(Logger.ERROR, errorString); + throw new IllegalStateException(errorString); + } + return defaultHandler; + } + + /** + * Finds a ModelHandler based on literal extension id. It's basically a + * "first found first returned". No specific order is guaranteed and the + * uniqueness of IDs is not considered. + * + * @param extensionId + * @return the given extension, or null + */ + private IModelHandler getHandlerExtension(String extensionId) { + IModelHandler found = null; + IConfigurationElement[] elements = reader.elements; + if (elements != null) { + for (int i = 0; i < elements.length; i++) { + String currentId = reader.getId(elements[i]); + if (extensionId.equals(currentId)) { + IModelHandler item = reader.getInstance(elements[i]); + found = item; + } + } + } + else if (Logger.DEBUG){ + Logger.log(Logger.WARNING, "There were no Model Handler found in registry"); //$NON-NLS-1$ + } + return found; + } + + /** + * Finds the registered IModelHandler for a given named file's content + * type. + * + * @param file + * @param provideDefault should the default extension be used in the absence of other methods + * @return The IModelHandler registered for the content type of the given + * file. If an exact match is not found, the most-specific match + * according to IContentType.isKindOf() will be returned. If none + * are found, either a default or null will be returned. + * @throws CoreException + */ + public IModelHandler getHandlerFor(IFile file, boolean provideDefault) throws CoreException { + IModelHandler modelHandler = null; + IContentDescription contentDescription = null; + IContentType contentType = null; + boolean accessible = file.isAccessible(); + if (accessible) { + /* Try the optimized method first as the description may be cached */ + contentDescription = file.getContentDescription(); + if (contentDescription != null) { + // use the provided description + contentType = contentDescription.getContentType(); + } + else { + /* use the more thorough discovery method to get a description */ + InputStream contents = null; + try { + contents = file.getContents(false); + contentDescription = Platform.getContentTypeManager().getDescriptionFor(contents, file.getName(), IContentDescription.ALL); + if (contentDescription != null) { + contentType = contentDescription.getContentType(); + } + } + catch (IOException e) { + // nothing further can be done, but will log for debugging + Logger.logException(e); + } + finally { + if (contents != null) { + try { + contents.close(); + } + catch (IOException e1) { + // nothing can be done + } + } + } + } + } + + /* + * If we couldn't get the content type from a description, try basing + * it on just the filename + */ + if (contentType == null) { + contentType = Platform.getContentTypeManager().findContentTypeFor(file.getName()); + } + + if (contentType != null) { + modelHandler = getHandlerForContentType(contentType); + } + else if (contentType == null && provideDefault) { + // hard coding for null content type + modelHandler = getHandlerExtension(INTERNAL_DEFAULT_EXTENSION); //$NON-NLS-1$ + } + + return modelHandler; + } + + /** + * Finds the registered IModelHandler for a given named file's content + * type. Will check for a default. + * + * @param file + * @return The IModelHandler registered for the content type of the given + * file. If an exact match is not found, the most-specific match + * according to IContentType.isKindOf() will be returned. If none + * are found, either a default or null will be returned. + * @throws CoreException + */ + public IModelHandler getHandlerFor(IFile file) throws CoreException { + return getHandlerFor(file, true); + } + + + /** + * Finds the registered IModelHandler for a given named InputStream. + * + * @param inputName + * @param inputStream + * @return The IModelHandler registered for the content type of the given + * input. If an exact match is not found, the most-specific match + * according to IContentType.isKindOf() will be returned. If none + * are found, either a default or null will be returned. + * @throws IOException + */ + public IModelHandler getHandlerFor(String inputName, InputStream inputStream) throws IOException { + InputStream iStream = Utilities.getMarkSupportedStream(inputStream); + IModelHandler modelHandler = null; + IContentType contentType = null; + if (inputStream != null) { + try { + iStream.mark(CodedIO.MAX_MARK_SIZE); + contentType = Platform.getContentTypeManager().findContentTypeFor(Utilities.getLimitedStream(iStream), inputName); + } + finally { + if (iStream != null && iStream.markSupported()) { + iStream.reset(); + } + } + + } + if (contentType == null) { + contentType = Platform.getContentTypeManager().findContentTypeFor(inputName); + } + // if all else failed, try to detect solely on contents; done last for + // performance reasons + if (contentType == null) { + contentType = Platform.getContentTypeManager().findContentTypeFor(Utilities.getLimitedStream(iStream), null); + } + modelHandler = getHandlerForContentType(contentType); + return modelHandler; + } + + /** + * Finds the registered IModelHandler for a given IContentType. + * + * @param contentType + * @return The IModelHandler registered for the given content type. If an + * exact match is not found, the most-specific match according to + * IContentType.isKindOf() will be returned. If none are found, + * either a default or null will be returned. + */ + private IModelHandler getHandlerForContentType(IContentType contentType) { + IModelHandler handler = null; + if (contentType != null) { + IConfigurationElement exactContentTypeElement = null; + IConfigurationElement kindOfContentTypeElement = null; + int kindOfContentTypeDepth = 0; + IConfigurationElement[] elements = reader.elements; + if (elements != null) { + for (int i = 0; i < elements.length && exactContentTypeElement == null; i++) { + String currentId = reader.getAssociatedContentTypeId(elements[i]); + IContentType associatedContentType = Platform.getContentTypeManager().getContentType(currentId); + if (contentType.equals(associatedContentType)) { + exactContentTypeElement = elements[i]; + } + else if (contentType.isKindOf(associatedContentType)) { + /* + * Update the kindOfElement variable only if this + * element's content type is "deeper" (depth test + * ensures the first content type is remembered) + */ + IContentType testContentType = associatedContentType; + int testDepth = 0; + while (testContentType != null) { + testDepth++; + testContentType = testContentType.getBaseType(); + } + if (testDepth > kindOfContentTypeDepth) { + kindOfContentTypeElement = elements[i]; + kindOfContentTypeDepth = testDepth; + } + } + } + } + else if (Logger.DEBUG){ + Logger.log(Logger.WARNING, "There were no Model Handler found in registry"); //$NON-NLS-1$ + } + if (exactContentTypeElement != null) { + handler = reader.getInstance(exactContentTypeElement); + } + else if (kindOfContentTypeElement != null) { + handler = reader.getInstance(kindOfContentTypeElement); + } + } + + if (handler == null) { + // temp hard coding for null content type arguments + handler = getHandlerExtension(INTERNAL_DEFAULT_EXTENSION); //$NON-NLS-1$ + } + return handler; + } + + /** + * Finds the registered IModelHandler for a given content type ID. No + * specific order is guaranteed and the uniqueness of IDs is not + * considered. + * + * @param contentType + * @return The IModelHandler registered for the given content type ID. If + * an exact match is not found, the most-specific match according + * to IContentType.isKindOf() will be returned. If none are found, + * either a default or null will be returned. + */ + public IModelHandler getHandlerForContentTypeId(String contentTypeId) { + IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeId); + return getHandlerForContentType(contentType); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerRegistryReader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerRegistryReader.java new file mode 100644 index 0000000..c6d611f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerRegistryReader.java
@@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import java.util.HashMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.AbstractModelHandler; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; + + +/** + * This class just converts what's in the plugins registry into a form more + * easily useable by others, the ContentTypeRegistry. + */ +class ModelHandlerRegistryReader { + private HashMap allReadyCreateInstances = new HashMap(); + protected String ATT_ASSOCIATED_CONTENT_TYPE = "associatedContentTypeId"; //$NON-NLS-1$ + protected String ATT_CLASS = "class"; //$NON-NLS-1$ + protected String ATT_DEFAULT = "default"; //$NON-NLS-1$ + protected String ATT_ID = "id"; //$NON-NLS-1$ + IConfigurationElement[] elements; + protected String EXTENSION_POINT_ID = "modelHandler"; //$NON-NLS-1$ + // + protected String PLUGIN_ID = "org.eclipse.wst.sse.core"; //$NON-NLS-1$ + protected String TAG_NAME = "modelHandler"; //$NON-NLS-1$ + + // + /** + * ContentTypeRegistryReader constructor comment. + */ + ModelHandlerRegistryReader() { + super(); + } + + String getAssociatedContentTypeId(IConfigurationElement element) { + String value = element.getAttribute(ATT_ASSOCIATED_CONTENT_TYPE); + return value; + } + + String getId(IConfigurationElement element) { + String idValue = element.getAttribute(ATT_ID); + return idValue; + } + + synchronized IModelHandler getInstance(IConfigurationElement element) { + // May need to reconsider, but for now, we'll assume all clients must + // subclass AbstractContentTypeIdentifier. Its easier and safer, for + // this + // low level "system" object. (That is, we can check and set "package + // protected" + // attributes. + AbstractModelHandler modelHandler = (AbstractModelHandler) allReadyCreateInstances.get(getId(element)); + if (modelHandler == null) { + try { + modelHandler = (AbstractModelHandler) element.createExecutableExtension(ATT_CLASS); + if (modelHandler != null) { + allReadyCreateInstances.put(getId(element), modelHandler); + String defaultValue = element.getAttribute(ATT_DEFAULT); + if (defaultValue != null && "true".equals(defaultValue)) //$NON-NLS-1$ + modelHandler.setDefault(true); + else + modelHandler.setDefault(false); + // TODO -- set and check attributes vs. created instance + //contentTypeIdentifier.setOrCheckId(element.getAttribute(ATT_ID)); + } + } catch (CoreException e) { + org.eclipse.wst.sse.core.internal.Logger.logException(e); + } + } + return modelHandler; + } + + public boolean isElementDefault(IConfigurationElement element) { + String defaultValue = element.getAttribute(ATT_DEFAULT); + if (defaultValue != null && "true".equals(defaultValue)) //$NON-NLS-1$ + return true; + else + return false; + } + + ModelHandlerRegistryReader readRegistry() { + IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); + IExtensionPoint point = extensionRegistry.getExtensionPoint(PLUGIN_ID, EXTENSION_POINT_ID); + if (point != null) { + // just remember the elements, so plugins don't have to + // be activated, unless extension matches those "of interest". + elements = point.getConfigurationElements(); + } + return this; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerUtility.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerUtility.java new file mode 100644 index 0000000..f0b2a4b --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/ModelHandlerUtility.java
@@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.EmbeddedTypeHandler; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IDocumentTypeHandler; + +/** + * + * Likely a temporary class to be replaced by plugin, eventually. + */ +public class ModelHandlerUtility { + + private static ModelHandlerRegistry contentTypeRegistry; + + public static IDocumentTypeHandler getContentTypeFor(String string) { + return getContentTypeRegistry().getHandlerForContentTypeId(string); + } + + private static ModelHandlerRegistry getContentTypeRegistry() { + if (contentTypeRegistry == null) { + contentTypeRegistry = ModelHandlerRegistry.getInstance(); + } + return contentTypeRegistry; + } + + public static EmbeddedTypeHandler getDefaultEmbeddedType() { + return getEmbeddedContentTypeFor("text/html"); //$NON-NLS-1$ + } + + public static EmbeddedTypeHandler getEmbeddedContentTypeFor(String string) { + EmbeddedTypeHandler instance = null; + instance = EmbeddedTypeRegistryImpl.getInstance().getTypeFor(string); + return instance; + } + + public ModelHandlerUtility() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/PluginContributedFactoryReader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/PluginContributedFactoryReader.java new file mode 100644 index 0000000..7d2e2dd --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/modelhandler/PluginContributedFactoryReader.java
@@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.modelhandler; + +import java.util.List; +import java.util.Vector; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IDocumentTypeHandler; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; + + +/** + * + * Clients can make use of IExecutableExtension to handle the optional adapter + * class and key. Typically, many clients use a typical pattern of providing + * an adapter class and key in their null argument constructor anyway, so + * they'd only have to use IExecutableExtension if the factory was for more + * than one. + */ +public class PluginContributedFactoryReader { + // protected final String ATTR_ADAPTERKEY = "adapterKeyClass"; + // //$NON-NLS-1$ + // protected final String ATTR_REGISTERADAPTER = "registerAdapters"; + // //$NON-NLS-1$ + private static PluginContributedFactoryReader reader = null; + + public synchronized static PluginContributedFactoryReader getInstance() { + if (reader == null) { + reader = new PluginContributedFactoryReader(); + } + return reader; + } + + protected final String ATTR_CLASS = "class"; //$NON-NLS-1$ + protected final String ATTR_CONTENTTYPE = "contentTypeIdentiferId"; //$NON-NLS-1$ + + protected final String EXTENSION_POINT_ID = "contentTypeFactoryContribution"; //$NON-NLS-1$ + protected final String TAG_NAME = "factory"; //$NON-NLS-1$ + + protected PluginContributedFactoryReader() { + super(); + } + + public List getFactories(IDocumentTypeHandler handler) { + return loadRegistry(handler.getId()); + } + + public List getFactories(String type) { + return loadRegistry(type); + } + + protected INodeAdapterFactory loadFactoryFromConfigurationElement(IConfigurationElement element, Object requesterType) { + INodeAdapterFactory factory = null; + if (element.getName().equals(TAG_NAME)) { + String contentType = element.getAttribute(ATTR_CONTENTTYPE); + if (!requesterType.equals(contentType)) + return null; + String className = element.getAttribute(ATTR_CLASS); + // String adapterKeyClass = element.getAttribute(ATTR_ADAPTERKEY); + // String registerAdapters = + // element.getAttribute(ATTR_REGISTERADAPTER); + + // if className is null, then no one defined the extension point + // for adapter factories + if (className != null) { + try { + factory = (INodeAdapterFactory) element.createExecutableExtension(ATTR_CLASS); + } catch (CoreException e) { + // if an error occurs here, its probably that the plugin + // could not be found/loaded + org.eclipse.wst.sse.core.internal.Logger.logException("Could not find class: " + className, e); //$NON-NLS-1$ + } catch (Exception e) { + // if an error occurs here, its probably that the plugin + // could not be found/loaded -- but in any case we just + // want + // to log the error and continue running and best we can. + org.eclipse.wst.sse.core.internal.Logger.logException("Could not find class: " + className, e); //$NON-NLS-1$ + } + // if (plugin != null) { + // factory = oldAttributesCode(element, factory, className, + // plugin); + // + } + } + + return factory; + } + + protected List loadRegistry(Object contentType) { + List factoryList = null; // new Vector(); + IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); + IExtensionPoint point = extensionRegistry.getExtensionPoint(SSECorePlugin.ID, EXTENSION_POINT_ID); + if (point != null) { + IConfigurationElement[] elements = point.getConfigurationElements(); + if (elements.length > 0) { + // this is called a lot, so don't create vector unless really + // needed + // TODO: could eventually cache in a hashtable, or something, + // to avoid repeat processing + factoryList = new Vector(); + for (int i = 0; i < elements.length; i++) { + INodeAdapterFactory factory = loadFactoryFromConfigurationElement(elements[i], contentType); + if (factory != null) + factoryList.add(factory); + } + } + } + return factoryList; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/parser/ContextRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/parser/ContextRegion.java new file mode 100644 index 0000000..caaafd5 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/parser/ContextRegion.java
@@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.parser; + + + +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + + +/** + * Regions of this class are intended specifically for XML/HTML/JSPs. Other + * languages may need their own subclasses. (See the updateModel method). + */ +public class ContextRegion implements ITextRegion { + protected int fLength; + + protected int fStart; + protected int fTextLength; + protected String fType; + + protected ContextRegion() { + super(); + } + + public ContextRegion(String newContext, int newStart, int newTextLength, int newLength) { + fType = newContext; + fStart = newStart; + fTextLength = newTextLength; + fLength = newLength; + } + + + public void adjust(int i) { + fStart += i; + + } + + public void adjustLength(int i) { + fLength += i; + } + + public void adjustStart(int i) { + fStart += i; + } + + public void adjustTextLength(int i) { + fTextLength += i; + + } + + boolean allLetterOrDigit(String changes) { + boolean result = true; + for (int i = 0; i < changes.length(); i++) { + // TO_DO_FUTURE: check that a Java Letter or Digit is + // the same thing as an XML letter or digit + if (!(Character.isLetterOrDigit(changes.charAt(i)))) { + result = false; + break; + } + } + return result; + } + + boolean allWhiteSpace(String changes) { + boolean result = true; + for (int i = 0; i < changes.length(); i++) { + if (!Character.isWhitespace(changes.charAt(i))) { + result = false; + break; + } + } + + return result; + } + + boolean canHandleAsLetterOrDigit(String changes, int requestStart, int lengthToReplace) { + boolean result = false; + // Make sure we are in a non-white space area + if ((requestStart <= (getTextEnd())) && (allLetterOrDigit(changes))) { + result = true; + } + return result; + } + + boolean canHandleAsWhiteSpace(String changes, int requestStart, int lengthToReplace) { + boolean result = false; + // if we are in the "white space" area of a region, then + // we don't want to handle, a reparse is needed. + // the white space region is consider anywhere that would + // leave whitespace between this character and the text part. + // and of course, we can insert whitespace in whitespace region + // + // if there is no whitespace in this region, no need to look further + if (getEnd() > getTextEnd()) { + // no need to add one to end of text, as we used to, since we + // change definition of length to equate to offset plus one. + if (requestStart > getTextEnd()) { + // ok, we are in the whitespace region, so we can't handle, + // unless + // we are just inserting whitespace. + if (allWhiteSpace(changes)) { + result = true; + } + else { + result = false; + } + + } + } + + return result; + } + + public boolean contains(int position) { + + return fStart <= position && position < fStart + fLength; + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public void setLength(int i) { + fLength = i; + } + + public void setStart(int i) { + fStart = i; + } + + public void setTextLength(int i) { + fTextLength = i; + } + + public void setType(String string) { + fType = string; + } + + public String toString() { + String className = getClass().getName(); + String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$ + String result = shortClassName + "--> " + getType() + ": " + getStart() + "-" + getTextEnd() + (getTextEnd() != getEnd() ? ("/" + getEnd()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + // NOTE: if the document held by any region has been updated and the + // region offsets have not + // yet been updated, the output from this method invalid. + return result; + } + + public StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // the four types we used to handle here, have all been moved to + // specific region classes. + // XML_TAG_ATTRIBUTE_VALUE + // XML_TAG_ATTRIBUTE_NAME + // XML_CONTENT + // XML_CDATA_TEXT + return null; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/parser/ForeignRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/parser/ForeignRegion.java new file mode 100644 index 0000000..9bb5a36 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/parser/ForeignRegion.java
@@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.parser; + + + +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; + + +public class ForeignRegion extends ContextRegion { + + private String language = null; + private String surroundingTag = null; + + public ForeignRegion(String newContext, int newStart, int newTextLength, int newLength) { + super(newContext, newStart, newTextLength, newLength); + } + + public ForeignRegion(String newContext, int newStart, int newTextLength, int newLength, String newLanguage) { + super(newContext, newStart, newTextLength, newLength); + setLanguage(newLanguage); + } + + /** + * + * @return java.lang.String + */ + public java.lang.String getLanguage() { + return language; + } + + /** + * @return java.lang.String + */ + public java.lang.String getSurroundingTag() { + return surroundingTag; + } + + /** + * + * @param newLanguage + * java.lang.String + */ + public void setLanguage(java.lang.String newLanguage) { + language = newLanguage; + } + + /** + * @param newSurroundingTag + * java.lang.String + */ + public void setSurroundingTag(java.lang.String newSurroundingTag) { + surroundingTag = newSurroundingTag; + } + + public String toString() { + return "FOREIGN: " + super.toString();//$NON-NLS-1$ + } + + public StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion flatnode, String changes, int requestStart, int lengthToReplace) { + org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent result = null; + int lengthDifference = org.eclipse.wst.sse.core.internal.util.Utilities.calculateLengthDifference(changes, lengthToReplace); + fLength += lengthDifference; + fTextLength += lengthDifference; + result = new RegionChangedEvent(flatnode.getParentDocument(), requester, flatnode, this, changes, requestStart, lengthToReplace); + + return result; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/preferences/CommonModelPreferenceNames.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/preferences/CommonModelPreferenceNames.java new file mode 100644 index 0000000..a7ef278 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/preferences/CommonModelPreferenceNames.java
@@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.preferences; + +/** + * @deprecated CommonModelPreferenceNames are now managed by each individual + * content type. (XXCorePreferenceNames) + * + */ +public interface CommonModelPreferenceNames { + String TAB_WIDTH = "tabWidth";//$NON-NLS-1$ + String LINE_WIDTH = "lineWidth";//$NON-NLS-1$ + String SPLIT_MULTI_ATTRS = "splitMultiAttrs";//$NON-NLS-1$ + String INDENT_USING_TABS = "indentUsingTabs";//$NON-NLS-1$ + String CLEAR_ALL_BLANK_LINES = "clearAllBlankLines";//$NON-NLS-1$ + + String TAG_NAME_CASE = "tagNameCase";//$NON-NLS-1$ + String ATTR_NAME_CASE = "attrNameCase";//$NON-NLS-1$ + + String FORMATTING_SUPPORTED = "formattingSupported";//$NON-NLS-1$ + + String PREFERRED_MARKUP_CASE_SUPPORTED = "preferredMarkupCaseSupported";//$NON-NLS-1$ + + String TASK_TAG_TAGS = "task-tag-tags"; //$NON-NLS-1$ + String TASK_TAG_PRIORITIES = "task-tag-priorities"; //$NON-NLS-1$ + String TASK_TAG_ENABLE = "task-tags"; //$NON-NLS-1$ + String TASK_TAG_PROJECTS_IGNORED = "task-tag-projects-toIgnore"; //$NON-NLS-1$ + + + /** + * these are preferences that should be inherited from the "embedded + * preference store" for example: if you ask for th OVERVIEW_RULER + * preference for JSP, you will automatically get the preference from the + * HTML preference store. + */ + String EMBEDDED_CONTENT_TYPE_PREFERENCES[] = {TAB_WIDTH, LINE_WIDTH, SPLIT_MULTI_ATTRS, INDENT_USING_TABS, CLEAR_ALL_BLANK_LINES, TAG_NAME_CASE, ATTR_NAME_CASE,}; + + String FORMATTING_PREFERENCES[] = {TAB_WIDTH, LINE_WIDTH, SPLIT_MULTI_ATTRS, INDENT_USING_TABS, CLEAR_ALL_BLANK_LINES,}; + + String AUTO = "Auto";//$NON-NLS-1$ + String UTF_8 = "UTF-8";//$NON-NLS-1$ + String ISO_8859_1 = "ISO-8859-1";//$NON-NLS-1$ + + int ASIS = 0; + int LOWER = 1; + int UPPER = 2; + + // cleanup preference names + String CLEANUP_TAG_NAME_CASE = "cleanupTagNameCase";//$NON-NLS-1$ + String CLEANUP_ATTR_NAME_CASE = "cleanupAttrNameCase";//$NON-NLS-1$ + String COMPRESS_EMPTY_ELEMENT_TAGS = "compressEmptyElementTags";//$NON-NLS-1$ + String INSERT_REQUIRED_ATTRS = "insertRequiredAttrs";//$NON-NLS-1$ + String INSERT_MISSING_TAGS = "insertMissingTags";//$NON-NLS-1$ + String QUOTE_ATTR_VALUES = "quoteAttrValues";//$NON-NLS-1$ + String FORMAT_SOURCE = "formatSource";//$NON-NLS-1$ + String CONVERT_EOL_CODES = "convertEOLCodes";//$NON-NLS-1$ + String CLEANUP_EOL_CODE = "cleanupEOLCode";//$NON-NLS-1$ + + String LAST_ACTIVE_PAGE = "lastActivePage";//$NON-NLS-1$ + + // need to put default tab width preference here so it is accessible by + // both model and editor + // this way, editor does not need to query model's default tab width + // preference + int DEFAULT_TAB_WIDTH = 4; +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/preferences/PreferenceInitializer.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/preferences/PreferenceInitializer.java new file mode 100644 index 0000000..7505633 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/preferences/PreferenceInitializer.java
@@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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.wst.sse.core.internal.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.wst.sse.core.internal.tasks.TaskTagPreferenceKeys; + +public class PreferenceInitializer extends AbstractPreferenceInitializer { + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences() + */ + public void initializeDefaultPreferences() { + IEclipsePreferences taskTagDefaults = new DefaultScope().getNode(TaskTagPreferenceKeys.TASK_TAG_NODE); + taskTagDefaults.putBoolean(TaskTagPreferenceKeys.TASK_TAG_ENABLE, false); + taskTagDefaults.put(TaskTagPreferenceKeys.TASK_TAG_TAGS, "TODO,FIXME,XXX"); //$NON-NLS-1$ + taskTagDefaults.put(TaskTagPreferenceKeys.TASK_TAG_PRIORITIES, "1,2,1"); //$NON-NLS-1$ + taskTagDefaults.put(TaskTagPreferenceKeys.TASK_TAG_CONTENTTYPES_IGNORED, ""); //$NON-NLS-1$ + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/propertytester/StructuredFilePropertyTester.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/propertytester/StructuredFilePropertyTester.java new file mode 100644 index 0000000..184bc03 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/propertytester/StructuredFilePropertyTester.java
@@ -0,0 +1,85 @@ +/****************************************************************************** + * Copyright (c) 2008 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.wst.sse.core.internal.propertytester; + +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.wst.sse.core.internal.Logger; + +/** + * A Property Tester that operates on IFiles and validates + * that the expected content type id matches that of the content + * type of the file, or any of the base content types. + * + * Based on org.eclipse.core.internal.propertytester.FilePropertyTester + * + * @deprecated use org.eclipse.core.resources.contentTypeId instead + * + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=288216 + */ +public class StructuredFilePropertyTester extends PropertyTester { + + /** + * A property indicating that we are looking to verify that the file matches + * the content type matching the given identifier. The identifier is + * provided as the expected value. + */ + private static final String PROPERTY_CONTENT_TYPE_ID = "contentTypeId"; //$NON-NLS-1$ + + /* + * (non-Javadoc) + * @see org.eclipse.core.expressions.IPropertyTester#test(java.lang.Object, java.lang.String, java.lang.Object[], java.lang.Object) + */ + public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { + if(PROPERTY_CONTENT_TYPE_ID.equals(property) && (expectedValue != null) && (receiver instanceof IFile) && ((IFile) receiver).exists()) + return testContentType((IFile) receiver, expectedValue.toString()); + return false; + } + + /** + * Tests whether the content type for <code>file</code> (or any base content types) + * matches the <code>contentTypeId</code>. It is possible that this method call could + * cause the file to be read. It is also possible (through poor plug-in + * design) for this method to load plug-ins. + * + * @param file + * The file for which the content type should be determined; must + * not be <code>null</code>. + * @param contentTypeId + * The expected content type; must not be <code>null</code>. + * @return <code>true</code> if the file's content type (or base content types) + * has an identifier that matches <code>contentTypeId</code>; + * <code>false</code> otherwise. + */ + private boolean testContentType(final IFile file, String contentTypeId) { + final String expectedValue = contentTypeId.trim(); + + try { + IContentDescription contentDescription = file.getContentDescription(); + if (contentDescription != null) { + IContentType contentType = contentDescription.getContentType(); + while (contentType != null) { + if (expectedValue.equals(contentType.getId())) + return true; + contentType = contentType.getBaseType(); + } + } + } + catch (Exception e) { + // [232831] - Log messages only when debugging + if(Logger.DEBUG) + Logger.logException(e); + } + return false; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/AbstractAdapterFactory.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/AbstractAdapterFactory.java new file mode 100644 index 0000000..4aa9c80 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/AbstractAdapterFactory.java
@@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +/** + * An abstract implementation of IAdapterFactory. All implementers of + * IAdapterFactory should subclass this class. The default constructor uses + * itself (this) as the key. Subclasses need to provide a way to create the + * adapter, and can override or call other methods. + */ +abstract public class AbstractAdapterFactory implements INodeAdapterFactory { + + + + private Object fAdapterKey; + + private boolean fShouldRegisterAdapter; + + /** + * If this default constructor used, setAdapterKey and setShouldRegister + * should be used to properly initialize. + */ + protected AbstractAdapterFactory() { + super(); + + } + + + public AbstractAdapterFactory(Object adapterKey) { + this(adapterKey, true); + } + + /** + * Suclasses may extended this constructor, if needed. + */ + + public AbstractAdapterFactory(Object adapterKey, boolean registerAdapters) { + + super(); + fAdapterKey = adapterKey; + fShouldRegisterAdapter = registerAdapters; + + } + + /** + * ISSUE: should be final. See those that implement it + * for complicating details and "unknowns". + */ + public INodeAdapter adapt(INodeNotifier target) { + INodeAdapter adapter = null; + if (target != null) { + adapter = target.getExistingAdapter(fAdapterKey); + if (adapter == null) { + adapter = adaptNew(target); + } + } + return adapter; + } + + /** + * Subclasses should normally implement their own 'copy' method. By + * default, we'll return the same instance, for convenience of those using + * singleton factories (which have no state, and so need to do anything on + * 'release'). + * + */ + public INodeAdapterFactory copy() { + return this; + } + + /** + * This method needs to return true of this factory is for adapters of + * type 'type'. It is required that it return true if 'equals' and this + * default behavior is provided by this super class. Clients may extend + * this behavior if more complex logic is required. + */ + public boolean isFactoryForType(Object type) { + return type.equals(fAdapterKey); + } + + /** + * Subclasses may need to "cleanup" their adapter factory, release + * adapters, resources, etc. Subclasses may extend this method if such + * clean up is required. Note: while current behavior is to do nothing, + * subclasses should not assume this would always be true, so should + * always call super.release at the end of their method. + */ + public void release() { + // default for superclass is to do nothing + } + + /** + * Only for special cases there the adapter key can be set in the + * constructor. It can be set here. If it is set more than, and + */ + final protected void setAdapterKey(Object key) { + if (fAdapterKey != null && !(fAdapterKey.equals(key))) + throw new IllegalStateException("INodeAdapter Key cannot be changed."); //$NON-NLS-1$ + fAdapterKey = key; + } + + /** + * Can be called by subclasses during 'adapt' process, but must not be + * overridden or reimplemented by subclasses. + * + * @param target + * @return + */ + protected final INodeAdapter adaptNew(INodeNotifier target) { + INodeAdapter adapter = createAdapter(target); + if (adapter != null) { + if (fShouldRegisterAdapter) { + target.addAdapter(adapter); + } + } + return adapter; + } + + /** + * Subclasses must implement this method. It is called by infrastructure + * when an instance is needed. It is provided the node notifier, which may + * or may not be relevent when creating the adapter. Note: the adapter + * does not have to literally be a new intance and is actually recommended + * to typically be a singleton for performance reasons. + * + * @param target + * @return + */ + abstract protected INodeAdapter createAdapter(INodeNotifier target); + + + protected final boolean isShouldRegisterAdapter() { + return fShouldRegisterAdapter; + } + + + protected final void setShouldRegisterAdapter(boolean shouldRegisterAdapter) { + // ISSUE: technically we probably should not allow this value to + // change, after initialization, but is not so easy to do, + // and I'm not sure its "worth" the protection. + fShouldRegisterAdapter = shouldRegisterAdapter; + } + + + protected final Object getAdapterKey() { + return fAdapterKey; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/AbstractNotifier.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/AbstractNotifier.java new file mode 100644 index 0000000..6f18471 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/AbstractNotifier.java
@@ -0,0 +1,240 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; + + + +/** + * AbstractNotifier is similar to (and based on) the EMF NotifierImpl class, + * but is not related to EMF per se. This class is simpler (that is, not as + * many functions). + * + * Implementers of this INodeNotifier must subclass this class. + */ +public abstract class AbstractNotifier implements INodeNotifier { + private final static int growthConstant = 3; + private int adapterCount = 0; + + private INodeAdapter[] fAdapters; + + /** + * AbstractNotifier constructor comment. + */ + public AbstractNotifier() { + super(); + } + + /** + * addAdapter method comment. + */ + public synchronized void addAdapter(INodeAdapter adapter) { + + if (adapter == null) + return; + ensureCapacity(adapterCount + 1); + fAdapters[adapterCount++] = adapter; + } + + private synchronized void ensureCapacity(int needed) { + if (fAdapters == null) { + // first time + fAdapters = new INodeAdapter[needed + growthConstant]; + return; + } + int oldLength = fAdapters.length; + if (oldLength < needed) { + INodeAdapter[] oldAdapters = fAdapters; + INodeAdapter[] newAdapters = new INodeAdapter[needed + growthConstant]; + System.arraycopy(oldAdapters, 0, newAdapters, 0, adapterCount); + fAdapters = newAdapters; + } + } + + /** + * NOT API: used only for testing. + * + * @return int + */ + public int getAdapterCount() { + return adapterCount; + } + + /** + * Default behavior for getting an adapter. + */ + public synchronized INodeAdapter getAdapterFor(Object type) { + // first, we'll see if we already have one + INodeAdapter result = getExistingAdapter(type); + // if we didn't find one in our list already, + // let's create it + if (result == null) { + FactoryRegistry reg = getFactoryRegistry(); + if (reg != null) { + INodeAdapterFactory factory = reg.getFactoryFor(type); + if (factory != null) { + result = factory.adapt(this); + } + } + // We won't prevent null from being returned, but it would be + // unusual. + // It might be because Factory is not working correctly, or + // not installed, so we'll allow warning message. + if ((result == null) && (org.eclipse.wst.sse.core.internal.util.Debug.displayWarnings)) { + System.out.println("Warning: no adapter was found or created for " + type); //$NON-NLS-1$ + } + } + return result; + } + + /** + * Returns a shallow clone of list, since clients should not manipulate + * our list directly. Instead, they should use add/removeAdapter. + */ + public synchronized Collection getAdapters() { + if (fAdapters != null) { + if (adapterCount == 0) { + fAdapters = null; + return Collections.EMPTY_LIST; + } + else { + // we need to make a new array, to be sure + // it doesn't contain nulls at end, which may be + // present there for "growth". + INodeAdapter[] tempAdapters = new INodeAdapter[adapterCount]; + System.arraycopy(fAdapters, 0, tempAdapters, 0, adapterCount); + // EMF uses the unmodifiableCollection. Its a bit of a + // performance + // drain, but may want to leave in since + // it would "fail fast" if someone was trying to modify the + // list. + return Collections.unmodifiableCollection(Arrays.asList(tempAdapters)); + // return Arrays.asList(newAdapters); + } + } + else + return Collections.EMPTY_LIST; + } + + private long getAdapterTimeCriteria() { + // to "re-get" the property each time is a little awkward, but we + // do it that way to avoid adding instance variable just for + // debugging. + // This method should only be called if debugAdapterNotifcationTime + // is true. + final String criteriaStr = Platform.getDebugOption("org.eclipse.wst.sse.core/dom/adapter/notification/time/criteria"); //$NON-NLS-1$ + long criteria = -1; + if (criteriaStr != null) { + try { + criteria = Long.parseLong(criteriaStr); + } + catch (NumberFormatException e) { + // catch to be sure we don't burb in notification loop, + // but ignore, since just a debug aid + } + } + return criteria; + } + + public synchronized INodeAdapter getExistingAdapter(Object type) { + INodeAdapter result = null; + for (int i = 0; i < adapterCount; i++) { + INodeAdapter a = fAdapters[i]; + if (a != null && a.isAdapterForType(type)) { + result = a; + break; + } + } + // if we didn't find one in our list, + // return the null result + return result; + } + + abstract public FactoryRegistry getFactoryRegistry(); + + public void notify(int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { + + int localAdapterCount = 0; + INodeAdapter[] localAdapters = null; + + // lock object while making local assignments + synchronized (this) { + if (fAdapters != null) { + localAdapterCount = adapterCount; + localAdapters = new INodeAdapter[localAdapterCount]; + System.arraycopy(fAdapters, 0, localAdapters, 0, localAdapterCount); + } + } + + for (int i = 0; i < localAdapterCount; i++) { + INodeAdapter a = localAdapters[i]; + + if (Logger.DEBUG_ADAPTERNOTIFICATIONTIME) { + long getAdapterTimeCriteria = getAdapterTimeCriteria(); + long startTime = System.currentTimeMillis(); + // ** keep this line identical with non-debug version!! + a.notifyChanged(this, eventType, changedFeature, oldValue, newValue, pos); + long notifyDuration = System.currentTimeMillis() - startTime; + if (getAdapterTimeCriteria >= 0 && notifyDuration > getAdapterTimeCriteria) { + System.out.println("adapter notifyDuration: " + notifyDuration + " class: " + a.getClass()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + else { + try { + // ** keep this line identical with debug version!! + a.notifyChanged(this, eventType, changedFeature, oldValue, newValue, pos); + } + catch (Exception e) { + // Its important to "keep going", since notifications + // occur between an + // aboutToChange event and a changed event -- the + // changed event typically being require + // to restore state, etc. So, we just log message, do + // not re-throw it, but + // typically the exception does indicate a serious + // program error. + Logger.logException("A structured model client, " + a + " threw following exception during adapter notification (" + INodeNotifier.EVENT_TYPE_STRINGS[eventType] + " )", e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + } + } + + public synchronized void removeAdapter(INodeAdapter a) { + if (fAdapters == null || a == null) + return; + int newIndex = 0; + INodeAdapter[] newAdapters = new INodeAdapter[fAdapters.length]; + int oldAdapterCount = adapterCount; + boolean found = false; + for (int oldIndex = 0; oldIndex < oldAdapterCount; oldIndex++) { + INodeAdapter candidate = fAdapters[oldIndex]; + if (a == candidate) { + adapterCount--; + found = true; + } + else + newAdapters[newIndex++] = fAdapters[oldIndex]; + } + if (found) + fAdapters = newAdapters; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/DocumentChanged.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/DocumentChanged.java new file mode 100644 index 0000000..6d7a4ec --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/DocumentChanged.java
@@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +import org.eclipse.wst.sse.core.internal.model.ModelLifecycleEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +public class DocumentChanged extends ModelLifecycleEvent { + private IStructuredDocument fNewDocument; + + private IStructuredDocument fOldDocument; + + protected DocumentChanged() { + + super(ModelLifecycleEvent.MODEL_DOCUMENT_CHANGED); + + } + + protected DocumentChanged(int additionalType, IStructuredModel model) { + + super(model, ModelLifecycleEvent.MODEL_DOCUMENT_CHANGED | additionalType); + + } + + public DocumentChanged(int additionalType, IStructuredModel model, IStructuredDocument oldDocument, IStructuredDocument newDocument) { + + this(additionalType, model); + fOldDocument = oldDocument; + fNewDocument = newDocument; + } + + public IStructuredDocument getNewDocument() { + + return fNewDocument; + } + + public IStructuredDocument getOldDocument() { + + return fOldDocument; + } + + void setNewDocument(IStructuredDocument newDocument) { + + fNewDocument = newDocument; + } + + void setOldDocument(IStructuredDocument oldDocument) { + + fOldDocument = oldDocument; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelLifecycleListener.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelLifecycleListener.java new file mode 100644 index 0000000..87f8353 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelLifecycleListener.java
@@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +import org.eclipse.wst.sse.core.internal.model.ModelLifecycleEvent; + +/** + * This is an early version of a class that may change over the next few + * milestones. + */ + +public interface IModelLifecycleListener { + + void processPostModelEvent(ModelLifecycleEvent event); + + void processPreModelEvent(ModelLifecycleEvent event); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelLoader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelLoader.java new file mode 100644 index 0000000..53308f0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelLoader.java
@@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + + +/** + * Responsible for creating a new Model from a resource, or as a new, empty + * instance. + * + */ +public interface IModelLoader { + /** + * This method should perform all the model initialization required before + * it contains content, namely, it should call newModel, the + * createNewStructuredDocument(), then setAdapterFactories. (this is + * tentative) + */ + IStructuredModel createModel(); + + /** + * Method createModel. Creates a new model based on old one. + * + * @param oldModel + * @return IStructuredModel + */ + IStructuredModel createModel(IStructuredModel oldModel); + + /** + * This method must return those factories which must be attached to the + * structuredModel before content is applied. + */ + List getAdapterFactories(); + + void load(IFile file, IStructuredModel model) throws IOException, CoreException; + + void load(InputStream inputStream, IStructuredModel model, EncodingRule encodingRule) throws IOException; + + void load(String filename, InputStream inputStream, IStructuredModel model, String encodingName, String lineDelimiter) throws IOException; + + IModelLoader newInstance(); + + /** + * This method should always return an new, empty Structured Model + * appropriate for itself. + */ + IStructuredModel newModel(); + + IStructuredModel reinitialize(IStructuredModel model); + + /** + * This method should get a fresh copy of the data, and repopulate the + * models ... normally by a call to setText on the structuredDocument, for + * StructuredModels. This method is needed in some cases where clients are + * sharing a model and then changes canceled. Say for example, one editor + * and several "displays" are sharing a model, if the editor is closed + * without saving changes, then the displays still need a model, but they + * should revert to the original unsaved version. + */ + void reload(InputStream inputStream, IStructuredModel model); + + /** + * Create a Structured Model with the given StructuredDocument instance as + * its document (instead of a new document instance as well) + */ + IStructuredModel createModel(IStructuredDocument document, String baseLocation, IModelHandler handler); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelManager.java new file mode 100644 index 0000000..b23d0fa --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelManager.java
@@ -0,0 +1,554 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.Enumeration; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.util.URIResolver; + +/** + * <p> + * Provides APIs for managing (get, release, save, and save as) SSE Structured + * Models. + * </p> + * <p> + * Structured Models created from an implementor of this interface can be + * either managed or unmanaged. Managed models are shared using reference + * counts, so until that count has been decremented to zero, the model will + * continue to exist in memory. When managed, models can be looked up using + * their IDs or their IStructuredDocuments, which can be advantageous when + * building on APIs that aren't specifically designed for SSE (such as those + * revolving around IDocuments). Unmanaged models offer no such features, and + * are largely used for tasks where their contents are ephemeral, such as for + * populating a source viewer with syntax-colored content. + * </p> + * <p> + * There are two types of access used when retrieving a model from the model + * manager: READ and EDIT. The contents of a model can be modified regardless + * of which access type is used, but any client who gets a model for EDIT is + * explicitly declaring that they are interested in saving those changed + * contents. The EDIT and READ reference counts are visible to everyone, as + * are convenience methods for determining whether a managed model is shared + * among multiple clients accessing it for READ or EDIT. + * </p> + * <p> + * Managed models whose contents are "dirty" with READ and EDIT counts above + * zero will be reverted to the on-disk content if the EDIT count drops to + * zero while the READ count remains above zero. + * </p> + * <p> + * Shared models for which the read and edit counts have both dropped to zero + * are no longer valid for use, regardless of whether they have been garbage + * collected or not. It is possible, but not guaranteed, that the underlying + * structured document is still valid and may even be used in constructing a + * new shared model. + * </p> + * <p> + * + * @noimplement This interface is not intended to be implemented by clients. + * Clients should obtain an instance of the IModelManager interface through + * {@link StructuredModelManager#getModelManager()}.</p> + * </p> + * <p> + * @see {@link StructuredModelManager}</p> + */ +public interface IModelManager { + + /** + * A fixed ID used for models which were created as duplicates of existing + * models + */ + public final static String DUPLICATED_MODEL = "org.eclipse.wst.sse.core.IModelManager.DUPLICATED_MODEL"; //$NON-NLS-1$ + + /** + * A fixed ID used for unmanaged models + */ + public final static String UNMANAGED_MODEL = "org.eclipse.wst.sse.core.IModelManager.UNMANAGED_MODEL"; //$NON-NLS-1$ + + /** + * Calculate id provides a common way to determine the id from the input + * ... needed to get and save the model. It is a simple class utility, but + * is an instance method so can be accessed via interface. + */ + public String calculateId(IFile file); + + /** + * Copies a model with the old id + * @param oldId - the old model's ID + * @param newId - the new model's ID + * @return the new model + * @throws ResourceInUse if the given new ID is already in use by a managed model + */ + IStructuredModel copyModelForEdit(String oldId, String newId) throws ResourceInUse; + + /** + * Creates a new, but empty, unmanaged model of the same kind as the one + * given. For a managed model with the same contents, use "copy". + * + * @param model + * @return the model, or null of one could not be created + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + public IStructuredModel createNewInstance(IStructuredModel model) throws IOException; + + /** + * Factory method, since a proper IStructuredDocument must have a proper + * parser assigned. If the resource does already exist, then + * createStructuredDocumentFor is the right API to use. + * + * @param iFile + * @return the document, or null if one could not be created + * @throws ResourceAlreadyExists + * if the IFile already exists + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + * @throws ResourceAlreadyExists if the give file already exists + */ + IStructuredDocument createNewStructuredDocumentFor(IFile iFile) throws ResourceAlreadyExists, IOException, CoreException; + + /** + * Factory method, since a proper IStructuredDocument must have a proper + * parser assigned. Note: clients should verify IFile exists before using + * this method. If this IFile does not exist, then + * {@link #createNewStructuredDocumentFor(IFile)} is the correct API to use. + * + * @param iFile - the file + * @return the document, or null if one could not be created + * + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + IStructuredDocument createStructuredDocumentFor(IFile iFile) throws IOException, CoreException; + + /** + * Convenience method, since a proper IStructuredDocument must have a + * proper parser assigned. It should only be used when an empty + * structuredDocument is needed. Otherwise, use IFile form. + * + * @param contentTypeId + * @return a structured document with the correct parsing setup for the + * given content type ID, or null if one could not be created or + * the given content type ID is unknown or unsupported + */ + IStructuredDocument createStructuredDocumentFor(String contentTypeId); + + /** + * @deprecated - use IFile form instead as the correct encoding and content rules may not be applied otherwise + * + * Creates and returns a properly configured structured document for the given contents with the given name + * + * @param filename - the filename, which may be used to guess the content type + * @param contents - the contents to load + * @param resolver - the URIResolver to use for locating any needed resources + * @return the IStructuredDocument or null of one could not be created + * @throws IOException if the file's contents can not be read or its content type can not be determined + */ + IStructuredDocument createStructuredDocumentFor(String filename, InputStream contents, URIResolver resolver) throws IOException; + + /** + * Creates and returns a properly configured structured document for the given contents with the given name + * + * @param filename - the filename, which may be used to guess the content type + * @param inputStream - the contents to load + * @param resolver - the URIResolver to use for locating any needed resources + * @param ianaEncodingName - the IANA specified encoding to use when reading the contents + * @return the IStructuredDocument or null if one could not be created + * @throws IOException if the file's contents can not be read or its content type can not be determined + * @deprecated - clients should convert the InputStream into text themselves + * and then use the version of this method taking a String for its + * content + */ + IStructuredDocument createStructuredDocumentFor(String filename, InputStream inputStream, URIResolver resolver, String ianaEncodingName) throws IOException; + + /** + * Creates and returns a properly configured structured document for the given contents with the given name + * + * @param filename - the filename, which may be used to guess the content type + * @param content - the contents to load + * @param resolver - the URIResolver to use for locating any referenced resources + * @return a structured document with the correct parsing setup for the + * given filename, or null if one could not be created + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + IStructuredDocument createStructuredDocumentFor(String filename, String content, URIResolver resolver) throws IOException; + + /** + * Creates and returns an unmanaged model populated with the given IFile's + * contents + * + * @param iFile + * @return a structured model, or null if one could not be created + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + IStructuredModel createUnManagedStructuredModelFor(IFile iFile) throws IOException, CoreException; + + /** + * Convenience method. It depends on the loader's newModel method to return + * an appropriate StrucuturedModel appropriately initialized. + * + * @param contentTypeId + * @return a structured model for the given content type, or null if one could not be created or the content type is unsupported + */ + IStructuredModel createUnManagedStructuredModelFor(String contentTypeId); + + /** + * @deprecated + */ + IStructuredModel createUnManagedStructuredModelFor(String contentTypeId, URIResolver resolver); + + /** + * Note: callers of this method must still release the model when finished. + * + * @param document + * @return the structured model containing the give document, incrementing + * its edit count, or null if there is not a model corresponding + * to this document. + */ + IStructuredModel getExistingModelForEdit(IDocument document); + + /** + * @param file + * @return the structured model for the given file, incrementing its edit + * count, or null if one does not already exist for this file. + */ + IStructuredModel getExistingModelForEdit(IFile file); + + /** + * @param id + * @return the structured model with the given ID, incrementing its edit + * count, or null if one does not already exist for this ID + */ + public IStructuredModel getExistingModelForEdit(Object id); + + /** + * Note: callers of this method must still release the model when finished. + * + * @param document + * @return the structured model containing the give document, incrementing + * its read count, or null if there is not a model corresponding + * to this document. + */ + IStructuredModel getExistingModelForRead(IDocument document); + + /** + * @param file + * @return the structured model for the given file, incrementing its read + * count, or null if one does not already exist for this file. + */ + public IStructuredModel getExistingModelForRead(IFile iFile); + + /** + * @param id + * @return the structured model with the given ID, incrementing its edit + * count, or null if one does not already exist for this ID + */ + public IStructuredModel getExistingModelForRead(Object id); + + /** + * @deprecated - internal information + */ + public Enumeration getExistingModelIds(); + + /** + * Returns a structured model for the given file. If one does not already + * exists, one will be created with an edit count of 1. If one already + * exists, its edit count will be incremented before it is returned. + * + * @param iFile + * @return a structured model for the given file, or null if one could not + * be found or created + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + public IStructuredModel getModelForEdit(IFile iFile) throws IOException, CoreException; + + /** + * Returns a structured model for the given file. If one does not already + * exists, one will be created with an edit count of 1. If one already + * exists, its edit count will be incremented before it is returned. + * + * @param iFile + * @param encodingRule the rule for handling encoding + * @return a structured model for the given file, or null if one could not + * be found or created + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + * @deprecated - encoding is handled automatically based on the file's + * contents or user preferences + */ + public IStructuredModel getModelForEdit(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * @deprecated - Encoding and the line delimiter used are handled + * automatically based on the file's contents or user + * preferences. + */ + public IStructuredModel getModelForEdit(IFile iFile, String encoding, String lineDelimiter) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * Returns a structured model for the given document. If one does not + * already exists, one will be created with an edit count of 1. If one + * already exists, its edit count will be incremented before it is + * returned. This method is intended only to interact with documents + * contained within File Buffers. + * + * @param textFileBufferDocument + * @return a structured model for the given document, or null if there is + * insufficient information known about the document instance to + * do so + */ + public IStructuredModel getModelForEdit(IStructuredDocument textFileBufferDocument); + + /** + * Returns a structured model for the given contents using the given ID. + * If one does not already exist, one will be created with an edit count + * of 1. If one already exists, its edit count will be incremented before + * it is returned. + * + * @param id + * - the id for the model + * @param inStream + * - the initial contents of the model + * @param resolver + * - the URIResolver to use for locating any needed resources + * @return a structured model for the given content, or null if one could + * not be found or created + * @throws UnsupportedEncodingException + * @throws IOException + * if the contents can not be read or its detected encoding + * does not support its contents + * @deprecated - a URI resolver should be automatically created when + * needed + */ + public IStructuredModel getModelForEdit(String id, InputStream inStream, URIResolver resolver) throws UnsupportedEncodingException, IOException; + + /** + * Returns a structured model for the given file. If one does not already + * exists, one will be created with a read count of 1. If one already + * exists, its read count will be incremented before it is returned. + * + * @param iFile + * @return a structured model for the given file, or null if one could not + * be found or created + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + public IStructuredModel getModelForRead(IFile iFile) throws IOException, CoreException; + + /** + * @deprecated - encoding is handled automatically based on the file's + * contents or user preferences + */ + public IStructuredModel getModelForRead(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * @deprecated - Encoding and the line delimiter used are handled + * automatically based on the file's contents or user + * preferences. + */ + public IStructuredModel getModelForRead(IFile iFile, String encoding, String lineDelimiter) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * Returns a structured model for the given document. If one does not + * already exists, one will be created with a read count of 1. If one + * already exists, its read count will be incremented before it is + * returned. This method is intended only to interact with documents + * contained within File Buffers. + * + * @param textFileBufferDocument + * @return a structured model for the given document, or null if there is + * insufficient information known about the document instance to + * do so + */ + public IStructuredModel getModelForRead(IStructuredDocument textFileBufferDocument); + + /** + * Returns a structured model for the given contents using the given ID. + * If one does not already exist, one will be created with an read count + * of 1. If one already exists, its read count will be incremented before + * it is returned. + * + * @param id + * - the id for the model + * @param inStream + * - the initial contents of the model + * @param resolver + * - the URIResolver to use for locating any needed resources + * @return a structured model for the given content, or null if one could + * not be found or created + * @throws UnsupportedEncodingException + * @throws IOException + * if the contents can not be read or its detected encoding + * does not support its contents + * @deprecated - a URI resolver should be automatically created when + * needed + */ + public IStructuredModel getModelForRead(String filename, InputStream inStream, URIResolver resolver) throws UnsupportedEncodingException, IOException; + + /** + * This method will not create a new model if it already exists ... if + * force is false. The idea is that a client should call this method once + * with force set to false. If the exception is thrown, then prompt client + * if they want to overwrite. + * + * @param iFile + * @param force + * @return the new structured model, or + * @throws ResourceInUse if the given new ID is already in use by a managed model + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + * @throws ResourceAlreadyExists if the give file already exists + */ + IStructuredModel getNewModelForEdit(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException; + + /** + * This method will not create a new model if it already exists ... if + * force is false. The idea is that a client should call this method once + * with force set to false. If the exception is thrown, then prompt client + * if they want to overwrite. + * + * @param iFile + * @param force + * @return the new structured model, or + * @throws ResourceInUse if the given new ID is already in use by a managed model + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + * @throws ResourceAlreadyExists if the give file already exists + */ + IStructuredModel getNewModelForRead(IFile iFile, boolean force) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException; + + /** + * This function returns the combined "read" and "edit" reference counts + * of underlying model. + * + * @param id + * Object The id of the model + * @deprecated - internal information that can be obtained from the model + * itself + */ + int getReferenceCount(Object id); + + /** + * This function returns the "edit" reference count of underlying model. + * + * @param id + * Object The id of the model + * @deprecated - internal information that can be obtained from the model itself + */ + int getReferenceCountForEdit(Object id); + + /** + * This function returns the "read" reference count of underlying model. + * + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + * @deprecated - internal information that can be obtained from the model itself + */ + int getReferenceCountForRead(Object id); + + /** + * This function returns true if there are other references to the + * underlying model. + * + * @param id + * Object The id of the model + */ + boolean isShared(Object id); + + /** + * This function returns true if there are other "edit" references to the + * underlying model. + * + * @param id + * Object The id of the model + */ + boolean isSharedForEdit(Object id); + + /** + * This function returns true if there are other "read" references to the + * underlying model. + * + * @param id + * Object The id of the model + */ + boolean isSharedForRead(Object id); + + /** + * @deprecated - not granular enough + * + * This method can be called to determine if the model manager is within a + * "aboutToChange" and "changed" sequence. + */ + public boolean isStateChanging(); + + /** + * This method changes the id of the model. + * + * TODO: try to refine the design + * not to use this function + * + * @deprecated + */ + void moveModel(Object oldId, Object newId); + + /** + * This method can be called when the content type of a model changes. It's + * assumed the contentType has already been changed, and this method uses + * the text of the old one, to repopulate the text of the new one. In + * theory, the actual instance could change, (e.g. using 'saveAs' to go + * from xml to dtd), but in practice, the intent of this API is to return + * the same instance, just using different handlers, adapter factories, + * etc. + */ + IStructuredModel reinitialize(IStructuredModel model) throws IOException; + + /** + * This is similar to the getModel method, except this method does not use + * the cached version, but forces the cached version to be replaced with a + * fresh, unchanged version. Note: this method does not change any + * reference counts. Also, if there is not already a cached version of the + * model, then this call is essentially ignored (that is, it does not put + * a model in the cache) and returns null. + * + * @deprecated + */ + IStructuredModel reloadModel(Object id, InputStream inStream) throws UnsupportedEncodingException; + + /** + * Saves the contents of the given structured document to the given file. If + * the document belongs to a managed model, that model will be saved and + * marked as non-dirty. + * + * @param structuredDocument + * - the structured document + * @param iFile + * - the file to save to + * @throws UnsupportedEncodingException + * @throws CoreException if the file's contents or description can not be read + * @throws IOException if the file's contents can not be read or its detected encoding does not support its contents + */ + void saveStructuredDocument(IStructuredDocument structuredDocument, IFile iFile) throws UnsupportedEncodingException, IOException, CoreException; +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelStateListener.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelStateListener.java new file mode 100644 index 0000000..b14e473 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IModelStateListener.java
@@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + + + +/** + * Interface for those wanting to listen to a model's state changing. + */ +public interface IModelStateListener { + + /** + * A model is about to be changed. This typically is initiated by one + * client of the model, to signal a large change and/or a change to the + * model's ID or base Location. A typical use might be if a client might + * want to suspend processing until all changes have been made. + */ + void modelAboutToBeChanged(IStructuredModel model); + + /** + * Signals that the changes foretold by modelAboutToBeChanged have been + * made. A typical use might be to refresh, or to resume processing that + * was suspended as a result of modelAboutToBeChanged. + */ + void modelChanged(IStructuredModel model); + + /** + * Notifies that a model's dirty state has changed, and passes that state + * in isDirty. A model becomes dirty when any change is made, and becomes + * not-dirty when the model is saved. + */ + void modelDirtyStateChanged(IStructuredModel model, boolean isDirty); + + /** + * A modelDeleted means the underlying resource has been deleted. The + * model itself is not removed from model management until all have + * released it. Note: baseLocation is not (necessarily) changed in this + * event, but may not be accurate. + */ + void modelResourceDeleted(IStructuredModel model); + + /** + * A model has been renamed or copied (as in saveAs..). In the renamed + * case, the two paramenters are the same instance, and only contain the + * new info for id and base location. + */ + void modelResourceMoved(IStructuredModel oldModel, IStructuredModel newModel); + + void modelAboutToBeReinitialized(IStructuredModel structuredModel); + + void modelReinitialized(IStructuredModel structuredModel); + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeAdapter.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeAdapter.java new file mode 100644 index 0000000..e88a24f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeAdapter.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +/** + * This interface allows nodes to be adapted. + * + * The main difference between this type of adapter (IAdaptable) and base + * adapter is that these adapters are notified of changes. + * + * @plannedfor 1.0 + */ + +public interface INodeAdapter { + + /** + * The infrastructure calls this method to determine if the adapter is + * appropriate for 'type'. Typically, adapters return true based on + * identity comparison to 'type', but this is not required, that is, the + * decision can be based on complex logic. + * + */ + boolean isAdapterForType(Object type); + + /** + * Sent to adapter when notifier changes. Each notifier is responsible for + * defining specific eventTypes, feature changed, etc. + * + * ISSUE: may be more evolvable if the argument was one big 'notifier + * event' instance. + */ + void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeAdapterFactory.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeAdapterFactory.java new file mode 100644 index 0000000..d6a2506 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeAdapterFactory.java
@@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +/** + * INodeNotifiers can be adapted by INodeAdapters. This factory interface + * provides a way to provide factories which are invoked by the infrastructure + * to manage this process, from creating, to adapting, to releasing, if + * required. + * + * @plannedfor 1.0 + * + */ +public interface INodeAdapterFactory { + + /** + * The primary purpose of an adapter factory is to create an adapter and + * associate it with an INodeNotifier. This adapt method Method that + * returns the adapter associated with the given object. The + * implementation of this method should call addAdapter on the adapted + * object with the correct instance of the adapter, if appropriate. + * + * Note: the instance of the adapter returned may be a singleton or not + * ... depending on the needs of the INodeAdapter ... but in general it is + * recommended for an adapter to be stateless, so the efficiencies of a + * singleton can be gained. + * + * @param object + * the node notifier to be adapted + */ + INodeAdapter adapt(INodeNotifier object); + + /** + * Unlike clone, this method may or may not return the same instance, such + * as in the case where the IAdapterFactory is intended to be a singleton. + * + * @return an instance of this adapter factory. + */ + public INodeAdapterFactory copy(); + + /** + * isFactoryForType is called by infrastructure to decide if this adapter + * factory is apporiate to use for an adapter request that specifies + * 'type'. + * + * @param type - + * same object used to identify/request adapters. + * @return true if factory is appropriate for type, false otherwise. + */ + boolean isFactoryForType(Object type); + + /** + * release is called by infrastructure when the factory registry is + * released (which is done when a structured model is released). This + * intened for the factory to be allowed to clean up any state information + * it may have. + * + * Note: while not recommended, due to performance reasons, if individual + * adapters need some cleanup (or need to be released) it is (typically) + * the responsibility of the adapter factory to track them, and initiate + * what ever clean up is needed. In other works, cleanup at the adatper + * level is not provided by infrastructure. + */ + public void release(); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeNotifier.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeNotifier.java new file mode 100644 index 0000000..a76fe75 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/INodeNotifier.java
@@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + + + +import java.util.Collection; + +/** + * INodeNotifiers and INodeAdapters form a collaboration that allows clients + * to use the typical adapter pattern but with notification added, that is, + * client's adapters will be notified when the nodeNotifier changes. + * + * @plannedfor 1.0 + */ + +public interface INodeNotifier { + + /** + * The change represents a non-structural change, sent to node notifier's + * parent. + */ + static final int CHANGE = 1; + /** + * The change represents an add event. + */ + static final int ADD = 2; + + /** + * The change represents a remove event. + */ + static final int REMOVE = 3; + + /** + * The change represents a structural change, sent to least-common parent + * of node notifiers involved in the structural change + */ + static final int STRUCTURE_CHANGED = 4; + + /** + * The change represents a notification to parent notifier than its + * contents have changed. + */ + static final int CONTENT_CHANGED = 5; + + + /** + * NOT API: these strings are for printing, such as during debugging + */ + static final String[] EVENT_TYPE_STRINGS = new String[]{"undefined", "CHANGE", "ADD", "REMOVE", "STRUCTURE_CHANGED", "CONTENT_CHANGED"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + + + /** + * Add an adapter of this notifier. + * + * @param adapter + * the adapter to be added + * + */ + void addAdapter(INodeAdapter adapter); + + /** + * Return an exisiting adapter of type "type" or if none found create a + * new adapter using a registered adapter factory + */ + INodeAdapter getAdapterFor(Object type); + + /** + * Return a read-only Collection of the Adapters to this notifier. + * + * @return collection of adapters. + */ + Collection getAdapters(); + + /** + * Return an exisiting adapter of type "type" or null if none found + */ + INodeAdapter getExistingAdapter(Object type); + + /** + * sent to adapter when its nodeNotifier changes. + */ + void notify(int eventType, Object changedFeature, Object oldValue, Object newValue, int pos); + + /** + * Remove an adapter of this notifier. If the adapter does not exist for + * this node notifier, this request is ignored. + * + * @param adapter + * the adapter to remove + */ + void removeAdapter(INodeAdapter adapter); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IStructuredModel.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IStructuredModel.java new file mode 100644 index 0000000..8669019 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IStructuredModel.java
@@ -0,0 +1,419 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager; +import org.eclipse.wst.sse.core.internal.util.URIResolver; + + +/** + * IStructuredModels are mainly interesting by their extensions and + * implementers. The main purposed of this abstraction is to provide a common + * means to manage models that have an associated structured document. + * + * <p> + * TODO: this interface needs ton of cleanup! + * </p> + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IStructuredModel extends IAdaptable { + + + /** + * This API allows clients to declare that they are about to make a + * "large" change to the model. This change might be in terms of content + * or it might be in terms of the model id or base location. + * + * Note that in the case of embedded calls, notification to listeners is + * sent only once. + * + * Note that the client who is making these changes has the responsibility + * to restore the model's state once finished with the changes. See + * getMemento and restoreState. + * + * The method isModelStateChanging can be used by a client to determine if + * the model is already in a change sequence. + * + * This method is a matched pair to changedModel, and must be called + * before changedModel. A client should never call changedModel without + * calling aboutToChangeModel first nor call aboutToChangeModel without + * calling changedModel later from the same Thread. + */ + void aboutToChangeModel(); + + void addModelLifecycleListener(IModelLifecycleListener listener); + + void addModelStateListener(IModelStateListener listener); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, int cursorPosition, int selectionLength); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label, int cursorPosition, int selectionLength); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label, String description); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label, String description, int cursorPosition, int selectionLength); + + /** + * This API allows a client controlled way of notifying all ModelEvent + * listeners that the model has been changed. This method is a matched + * pair to aboutToChangeModel, and must be called after aboutToChangeModel + * ... or some listeners could be left waiting indefinitely for the + * changed event. So, its suggested that changedModel always be in a + * finally clause. Likewise, a client should never call changedModel + * without calling aboutToChangeModel first. + * + * In the case of embedded calls, the notification is just sent once. + * + */ + void changedModel(); + + long computeModificationStamp(IResource resource); + + /** + * @deprecated + * @see IModelManager#copyModelForEdit(String, String) + */ + IStructuredModel copy(String id) throws ResourceInUse, ResourceAlreadyExists; + + /** + * Disable undo management. + * + * @deprecated - the ability to enable and disable Undo management for the + * model cannot be guaranteed as it implicitly requires + * knowledge of the underlying undo/redo implementation + */ + void disableUndoManagement(); + + /** + * Enable undo management. + * + * @deprecated - the ability to enable and disable Undo management for the + * model cannot be guaranteed as it implicitly requires + * knowledge of the underlying undo/redo implementation + */ + void enableUndoManagement(); + + /** + * End recording undo transactions. + */ + void endRecording(Object requester); + + /** + * End recording undo transactions. + */ + void endRecording(Object requester, int cursorPosition, int selectionLength); + + /** + * This is a client-defined value for what that client (and/or loader) + * considers the "base" of the structured model. Frequently the location + * is either a workspace root-relative path of a workspace resource or an + * absolute location in the local file system. + * + * @return the base location of the model or <code>null</code> when one + * has not been set, such as in models resulting from calling + * {@link IModelManager#createNewInstance(IStructuredModel)} + */ + String getBaseLocation(); + + /** + * @return The associated content type identifier (String) for this model. + * This value may be more accurate than the content type against + * which the model handler was registered. + * + * @see IModelHandler#getAssociatedContentTypeId() + */ + String getContentTypeIdentifier(); + + /** + * + * @return The model's FactoryRegistry. A model is not valid without one. + */ + FactoryRegistry getFactoryRegistry(); + + /** + * The id is the id that the model manager uses to identify this model + */ + String getId(); + + /** + * @param offset + * a text offset within the structured document + * @return an IndexedRegion containing this offset or null if one could + * not be found + */ + IndexedRegion getIndexedRegion(int offset); + + /** + * @return the model's handler + */ + IModelHandler getModelHandler(); + + IModelManager getModelManager(); + + /** + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + * + * @return the reference count of underlying model + */ + int getReferenceCount(); + + /** + * This function returns the edit-responsible reference count of + * underlying model. + * + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + */ + int getReferenceCountForEdit(); + + /** + * This function returns the reader reference count of underlying model. + * + * @param id + * Object The id of the model TODO: try to refine the design + * not to use this function + */ + int getReferenceCountForRead(); + + Object getReinitializeStateData(); + + /** + * Get URI resolution helper + * + * @deprecated - use org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin.createResolver(*) instead + */ + URIResolver getResolver(); + + IStructuredDocument getStructuredDocument(); + + /** + * modification date of underlying resource, when this model was open, or + * last saved. (Note: for this version, the client must manage the + * accuracy of this data) + */ + long getSynchronizationStamp(); + + /** + * Get undo manager. + */ + IStructuredTextUndoManager getUndoManager(); + + /** + * + */ + boolean isDirty(); + + /** + * This method can be called to determine if the model is within a + * "aboutToChange" and "changed" sequence. + */ + public boolean isModelStateChanging(); + + /** + * + */ + boolean isNew(); + + boolean isReinitializationNeeded(); + + /** + * This is a combination of if the model is dirty and if the model is + * shared for write access. The last writer as the responsibility to be + * sure the user is prompted to save. + */ + public boolean isSaveNeeded(); + + /** + * This function returns true if either isSharedForRead or isSharedForEdit + * is true. + */ + boolean isShared(); + + /** + * This function returns true if there are other references to the + * underlying model. + */ + boolean isSharedForEdit(); + + /** + * This function returns true if there are other references to the + * underlying model. + */ + boolean isSharedForRead(); + + /** + * newInstance is similar to clone, except that the newInstance contains + * no content. Its purpose is so clients can get a temporary, unmanaged, + * model of the same "type" as the original. Note: the client may still + * need to do some initialization of the model returned by newInstance, + * depending on desired use. For example, the only factories in the + * newInstance are those that would be normally be created for a model of + * the given contentType. Others are not copied automatically, and if + * desired, should be added by client. + */ + IStructuredModel newInstance() throws IOException; + + /** + * Performs a reinitialization procedure. For this model. Note for future: + * there may be a day where the model returned from this method is a + * different instance than the instance it was called on. This will occur + * when there is full support for "save as" type functions, where the + * model could theoretically change completely. + */ + IStructuredModel reinit() throws IOException; + + /** + * This function allows the model to free up any resources it might be + * using. In particular, itself, as stored in the IModelManager. + * + */ + void releaseFromEdit(); + + /** + * This function allows the model to free up any resources it might be + * using. In particular, itself, as stored in the IModelManager. + * + */ + void releaseFromRead(); + + /** + * This function replenishes the model with the resource without saving + * any possible changes. It is used when one editor may be closing, and + * specifically says not to save the model, but another "display" of the + * model still needs to hang on to some model, so needs a fresh copy. + * + * Only valid for use with managed models. + */ + IStructuredModel reload(InputStream inputStream) throws IOException; + + void removeModelLifecycleListener(IModelLifecycleListener listener); + + void removeModelStateListener(IModelStateListener listener); + + /** + * A method that modifies the model's synchronization stamp to match the + * resource. Turns out there's several ways of doing it, so this ensures a + * common algorithm. + */ + void resetSynchronizationStamp(IResource resource); + + void resourceDeleted(); + + void resourceMoved(IStructuredModel newModel); + + void save() throws UnsupportedEncodingException, IOException, CoreException; + + /** + * @deprecated - will save according to the encoding priorities specified for the IFile + */ + void save(EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException; + + void save(IFile iFile) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * @deprecated - will save according to the encoding priorities specified for the IFile + */ + void save(IFile iFile, EncodingRule encodingRule) throws UnsupportedEncodingException, IOException, CoreException; + + void save(OutputStream outputStream) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * Sets the base location of this model to the new value, also updating + * the value in its URI resolution helper if one is present. + * + * @param newBaseLocation + */ + void setBaseLocation(String newBaseLocation); + + public void setDirtyState(boolean dirtyState); + + void setFactoryRegistry(FactoryRegistry registry); + + /** + * The id is the id that the model manager uses to identify this model + */ + void setId(String id) throws ResourceInUse; + + void setModelHandler(IModelHandler modelHandler); + + void setModelManager(IModelManager modelManager); + + public void setNewState(boolean newState); + + /** + * Sets a "flag" that reinitialization is needed. + */ + void setReinitializeNeeded(boolean b); + + /** + * Holds any data that the reinitialization procedure might find useful in + * reinitializing the model. This is handy, since the reinitialization may + * not take place at once, and some "old" data may be needed to properly + * undo previous settings. Note: the parameter was intentionally made to + * be of type 'Object' so different models can use in different ways. + */ + void setReinitializeStateData(Object object); + + /** + * Set the URI resolution helper + */ + void setResolver(URIResolver uriResolver); + + void setStructuredDocument(IStructuredDocument structuredDocument); + + /** + * Set undo manager. + */ + void setUndoManager(IStructuredTextUndoManager undoManager); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IndexedRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IndexedRegion.java new file mode 100644 index 0000000..a9e9538 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/IndexedRegion.java
@@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional; + + + +/** + * This type is used to indicate positions and lengths in source. Notice that + * while getEndOffset and getLength are redundant, given that + * + * <pre> + * <code> + * getEndOffset() == getStartOffset() + getLength(); + * </code> + * </pre> + * + * we provide (require) both since in some cases implementors may be able to + * provide one or the other more efficiently. + * + * Note: it is not part of the API contract that implementors of IndexedRegion -- + * as a whole collection for a particular source -- must completely cover the + * original source. They currently often do, so thought I'd mention explicitly + * this may not always be true. + * + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IndexedRegion { + + /** + * Can be used to test if the indexed regions contains the test position. + * + * @param testPosition + * @return true if test position is greater than or equal to start offset + * and less than start offset plus length. + */ + boolean contains(int testPosition); + + /** + * Can be used to get end offset of source text, relative to beginning of + * documnt. Implementers should return -1 if, or some reason, the region + * is not valid. + * + * @return endoffset + */ + int getEndOffset(); + + /** + * Can be used to get source postion of beginning of indexed region. + * Implementers should return -1 if, or some reason, the region is not + * valid. + * + * @return int position of start of index region. + */ + int getStartOffset(); + + /** + * Can be used to get the length of the source text. Implementers should + * return -1 if, or some reason, the region is not valid. + * + * @return int position of length of index region. + */ + int getLength(); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/StructuredModelManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/StructuredModelManager.java new file mode 100644 index 0000000..61e0718 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/StructuredModelManager.java
@@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2004, 2011 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.wst.sse.core.internal.provisional; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.eclipse.wst.sse.core.internal.model.ModelManagerImpl; +import org.osgi.framework.Bundle; + +/** + * Class to allow access to ModelManager. Not intended to be subclassed. + * + * @deprecated - use {@link org.eclipse.wst.sse.core.StructuredModelManager} instead + */ +final public class StructuredModelManager { + /** + * Do not allow instances to be created. + */ + private StructuredModelManager() { + super(); + } + + /** + * Provides access to the instance of IModelManager. Returns null if model + * manager can not be created or is not valid (such as, when workbench is + * shutting down). + * + * @return IModelManager - returns the one model manager for structured + * model + * @deprecated - use the one that is in + * org.eclipse.wst.sse.core.StructuredModelManager + */ + public static IModelManager getModelManager() { + boolean isReady = false; + IModelManager modelManager = null; + while (!isReady) { + Bundle localBundle = Platform.getBundle(SSECorePlugin.ID); + int state = localBundle.getState(); + if (state == Bundle.ACTIVE) { + isReady = true; + // getInstance is a synchronized static method. + modelManager = ModelManagerImpl.getInstance(); + } + else if (state == Bundle.STARTING) { + try { + Thread.sleep(100); + } + catch (InterruptedException e) { + // ignore, just loop again + } + } + else if (state == Bundle.STOPPING || state == Bundle.UNINSTALLED) { + isReady = true; + modelManager = null; + } + else { + // not sure about other states, 'resolved', 'installed' + isReady = true; + modelManager = null; + } + } + return modelManager; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/document/IEncodedDocument.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/document/IEncodedDocument.java new file mode 100644 index 0000000..2d64715 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/document/IEncodedDocument.java
@@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.document; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; + +/** + * This interface is strictly to define important "document properties" not + * found in IDocument, but not central to "StructuredDocument". + * + * Its not to be be implmented by clients. + * + * @plannedfor 1.0 + */ + +public interface IEncodedDocument extends IDocument { + + /** + * Returns the encoding memento for this document. + * + * @return the encoding memento for this document. + */ + EncodingMemento getEncodingMemento(); + + /** + * Returns the preferred line delimiter for this document. + */ + String getPreferredLineDelimiter(); + + /** + * Sets the encoding memento for this document. + * + * Is not to be called by clients, only document creation classes. + * + * @param localEncodingMemento + */ + void setEncodingMemento(EncodingMemento localEncodingMemento); + + /** + * Sets the preferredLineDelimiter. Is not to be called by clients, only + * document creation classes. + * + * @param probableLineDelimiter + */ + void setPreferredLineDelimiter(String probableLineDelimiter); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/document/IStructuredDocumentProposed.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/document/IStructuredDocumentProposed.java new file mode 100644 index 0000000..0c0f47d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/document/IStructuredDocumentProposed.java
@@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.document; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.Position; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.provisional.events.NewDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; + + +/** + * A IStructuredDocument is a collection of StructuredDocumentRegions. It's + * often called a "flat model" because its does contain some structural + * information, but not very much, usually, at most, a few levels of + * containment. + * + * Clients should not implement. + * + * @deprecated - was never used + */ +public interface IStructuredDocumentProposed extends IDocument, IDocumentExtension, IAdaptable { + + /** + * The document changing listeners receives the same events as the + * document listeners, but the difference is the timing and + * synchronization of data changes and notifications. + */ + void addDocumentChangingListener(IDocumentListener listener); + + /** + * this API ensures that any portion of the document within startOff to + * length is not readonly (that is, that its editable). Note that if the + * range overlaps with other readonly regions, those other readonly + * regions will be adjusted. + * + * @param startOffset + * @param length + */ + void clearReadOnly(int startOffset, int length); + + /** + * returns true if any portion of startOffset to length is readonly + * + * @param startOffset + * @param length + * @return + */ + boolean containsReadOnly(int startOffset, int length); + + /** + * Returns the region contained by offset. + * + * @param offset + * @return + */ + IStructuredDocumentRegion getRegionAtCharacterOffset(int offset); + + /** + * Resturns a list of the structured document regions. + * + * Note: possibly expensive call, not to be used casually. + * + * @return a list of the structured document regions. + */ + IStructuredDocumentRegionList getRegionList(); + + + /** + * Returns the text of this document. + * + * Same as 'get' in super class, added for descriptiveness. + * + * @return the text of this document. + */ + String getText(); + + /** + * causes that portion of the document from startOffset to length to be + * marked as readonly. Note that if this range overlaps with some other + * region with is readonly, the regions are effectivly combined. + * + * @param startOffset + * @param length + */ + void makeReadOnly(int startOffset, int length); + + /** + * newInstance is similar to clone, except it contains no data. One + * important thing to duplicate is the parser, with the parser correctly + * "cloned", including its tokeninzer, block tags, etc. + * + * NOTE: even after obtaining a 'newInstance' the client may have to do + * some initialization, for example, it may need to add its own model + * listeners. Or, as another example, if the IStructuredDocument has a + * parser of type StructuredDocumentRegionParser, then the client may need + * to add its own StructuredDocumentRegionHandler to that parser, if it is + * in fact needed. + */ + IStructuredDocumentProposed newInstance(); + + /** + * The document changing listeners receives the same events as the + * document listeners, but the difference is the timing and + * synchronization of data changes and notifications. + */ + void removeDocumentChangingListener(IDocumentListener listener); + + /** + * One of the APIs to manipulate the IStructuredDocument. + * + * replaceText replaces the text from oldStart to oldEnd with the new text + * found in the requestedChange string. If oldStart and oldEnd are equal, + * it is an insertion request. If requestedChange is null (or empty) it is + * a delete request. Otherwise it is a replace request. + * + * Similar to 'replace' in super class. + */ + StructuredDocumentEvent replaceText(Object requester, int oldStart, int replacementLength, String requestedChange); + + /** + * Note, same as replaceText API, but will allow readonly areas to be + * replaced. This method is not to be called by clients, only + * infrastructure. For example, one case where its ok is with undo + * operations (since, presumably, if user just did something that + * happended to involve some inserting readonly text, they should normally + * be allowed to still undo that operation. There might be other cases + * where its used to give the user a choice, e.g. "you are about to + * overwrite read only portions, do you want to continue". + */ + StructuredDocumentEvent overrideReadOnlyreplaceText(Object requester, int oldStart, int replacementLength, String requestedChange); + + /** + * One of the APIs to manipulate the IStructuredDocument in terms of Text. + * + * The setText method replaces all text in the model. + * + * @param requester - + * the object requesting the document be created. + * @param allText - + * all the text of the document. + * @return NewDocumentEvent - besides causing this event to be sent to + * document listeners, the event is returned. + */ + NewDocumentEvent setText(Object requester, String allText); + + /** + * Returns the encoding memento for this document. + * + * @return the encoding memento for this document. + */ + EncodingMemento getEncodingMemento(); + + /** + * Returns the line delimiter detected when this document was read from + * storage. + * + * @return line delimiter detected when this document was read from + * storage. + */ + String getDetectedLineDelimiter(); + + /** + * Sets the encoding memento for this document. + * + * Is not to be called by clients, only document creation classes. + * + * @param localEncodingMemento + */ + void setEncodingMemento(EncodingMemento localEncodingMemento); + + /** + * Sets the detected line delimiter when the document was read. Is not to + * be called by clients, only document creation classes. + * + * @param probableLineDelimiter + */ + void setDetectedLineDelimiter(String probableLineDelimiter); + + /** + * This function provides a way for clients to compare a string with a + * region of the documnet, without having to retrieve (create) a string + * from the document, thus more efficient of lots of comparisons being + * done. + * + * @param ignoreCase - + * if true the characters are compared based on identity. If + * false, the string are compared with case accounted for. + * @param testString - + * the string to compare. + * @param documentOffset - + * the document in the offset to start the comparison. + * @param length - + * the length in the document to compare (Note: technically, + * clients could just provide the string, and we could infer + * the length from the string supplied, but this leaves every + * client to correctly not even ask us if the the string length + * doesn't match the expected length, so this is an effort to + * maximize performance with correct code. + * @return true if matches, false otherwise. + */ + boolean stringMatches(boolean ignoreCase, String testString, int documentOffset, int length); + + Position createPosition(int offset, String category, String type); + + Position createPosition(int offset, int length, String category, String type); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/AboutToBeChangedEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/AboutToBeChangedEvent.java new file mode 100644 index 0000000..e549692 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/AboutToBeChangedEvent.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +/** + * This event is send to structured document listeners. It is perfectly + * analagous to its corresponding jface DocumentEvent and is provided simply + * to allow clients to distinguish the source of the event. + * + * @plannedfor 1.0 + */ +public class AboutToBeChangedEvent extends StructuredDocumentEvent { + + + /** + * Creates an instance of this event. + * + * @param document + * document involved in the change + * @param originalRequester + * source of original request + * @param changes + * the text changes + * @param offset + * offset of request + * @param lengthToReplace + * amount, if any, of replaced text + */ + public AboutToBeChangedEvent(IStructuredDocument document, Object originalRequester, String changes, int offset, int lengthToReplace) { + super(document, originalRequester, changes, offset, lengthToReplace); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/IModelAboutToBeChangedListener.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/IModelAboutToBeChangedListener.java new file mode 100644 index 0000000..df44547 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/IModelAboutToBeChangedListener.java
@@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + + + +/** + * Clients can implement this interface, and register with the + * structuredDocument using addAboutToBeChangedListner to be notified that the + * structuredDocument is about to be changed, but hasn't been changed at the + * time this event is fired. + */ +public interface IModelAboutToBeChangedListener { + + void modelAboutToBeChanged(AboutToBeChangedEvent structuredDocumentEvent); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/IStructuredDocumentListener.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/IStructuredDocumentListener.java new file mode 100644 index 0000000..26535af --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/IStructuredDocumentListener.java
@@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +/** + * @deprecated will be removed since we now subclass DocumentEvent. + */ + +public interface IStructuredDocumentListener { + + public void newModel(NewDocumentEvent structuredDocumentEvent); + + public void noChange(NoChangeEvent structuredDocumentEvent); + + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent); + + public void regionChanged(RegionChangedEvent structuredDocumentEvent); + + public void regionsReplaced(RegionsReplacedEvent structuredDocumentEvent); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NewDocumentContentEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NewDocumentContentEvent.java new file mode 100644 index 0000000..038db77 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NewDocumentContentEvent.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +/** + * The NewDocumentContentEvent is fired when an instance of a + * IStructuredDocument replaces all of its text. + * + * ISSUE: not currently used, but there's still some efficiencies to be had so + * plan to implement. + * + * @plannedfor 1.0 + */ +public class NewDocumentContentEvent extends NewDocumentEvent { + /** + * Creates an instance of this event. + * + * @param document + * the document being changed + * @param originalRequester + * the original requester of the change + */ + public NewDocumentContentEvent(IStructuredDocument document, Object originalRequester) { + super(document, originalRequester); + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NewDocumentEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NewDocumentEvent.java new file mode 100644 index 0000000..fe9a73a --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NewDocumentEvent.java
@@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +/** + * The NewDocumentEvent is fired when an instance of a IStructuredDocument + * sets or replaces all of its text. + * + * ISSUE: need to change so this is used for 'set' only. + * + * @plannedfor 1.0 + */ +public class NewDocumentEvent extends StructuredDocumentEvent { + + + /** + * Creates a new instance of the NewDocumentEvent. + * + * @param document + * being changed + * @param originalRequester + * source of request + */ + public NewDocumentEvent(IStructuredDocument document, Object originalRequester) { + super(document, originalRequester); + } + + /** + * This returns the length of the new document. + * + * @return int returns the length of the new document. + */ + public int getLength() { + return getStructuredDocument().getLength(); + } + + /** + * This doesn't mean quite the same thing as the IStructuredDocument + * Events in the super class. It always will return zero. + * + * @return int for a newDocument, the offset of is always 0 + */ + public int getOffset() { + return 0; + } + + /** + * For a new document, the text involved is the complete text. + * + * @return String the text that is the complete text of the documnet. + */ + public String getText() { + String results = getStructuredDocument().getText(); + return results; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NoChangeEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NoChangeEvent.java new file mode 100644 index 0000000..d5004bc --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/NoChangeEvent.java
@@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +/** + * This event is sent if, after analysis, it is found there is no reason to + * change the structuredDocument. This might occur, for example, if someone + * pastes in the exact same text that they are replacing, or if someone tries + * to change a read-only region. + * + * This might be important, for example, if some state variables are set on an + * "about the change" event. then if there is no change (i.e.no other event is + * fired), those state variables could reset, or whatever, upon receiving this + * event. + * + * @plannedfor 1.0 + */ +public class NoChangeEvent extends StructuredDocumentEvent { + /** + * NO_CONTENT_CHANGE means that a some text was requested to be replaced + * with identical text, so no change is actually done. + */ + public final static int NO_CONTENT_CHANGE = 2; + /** + * NO_EVENT is used in rare error conditions, when, basically, a request + * to change the document is made before the previous request has + * completed. This event to used so aboutToChange/Changed cycles can + * complete as required, but the document is most likely not modified as + * expected. + */ + public final static int NO_EVENT = 8; + /** + * READ_ONLY_STATE_CHANGE means that the "read only" state of the text was + * changed, not the content itself. + */ + public final static int READ_ONLY_STATE_CHANGE = 4; + + /** + * set to one of the above detailed reasons for why no change was done. + */ + public int reason = 0; + + /** + * NoChangeEvent constructor. This event can occur if there was a request + * to modify a document or its properties, but there as not really is no + * change to a document's content. + * + * @param source + * @param originalSource + * java.lang.Object + * @param changes + * java.lang.String + * @param offset + * int + * @param lengthToReplace + * int + */ + public NoChangeEvent(IStructuredDocument source, Object originalSource, String changes, int offset, int lengthToReplace) { + super(source, originalSource, changes, offset, lengthToReplace); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/RegionChangedEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/RegionChangedEvent.java new file mode 100644 index 0000000..29d4381 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/RegionChangedEvent.java
@@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + +/** + * This event is used when a document region changes in a non-structural way. + * Non-structural, that is, as far as the IStructuredDocument is concerned. + * The whole region, along with the new text is sent, just in case a listener + * (e.g. a tree model) might make its own determination of what to do, and + * needs the whole region to act appropriately. + * + * Note: users should not make assumptions about whether the region is + * literally the same instance or not -- it is currently a different instance + * that is identical to the old except for the changed region, but this + * implementation may change. + * + * @plannedfor 1.0 + */ +public class RegionChangedEvent extends StructuredDocumentEvent { + private ITextRegion fChangedRegion; + private IStructuredDocumentRegion fStructuredDocumentRegion; + + /** + * Creates instance of a RegionChangedEvent. + * + * @param document + * the document being changed. + * @param originalRequester + * the object making the request for the change. + * @param structuredDocumentRegion + * the containing region + * @param changedRegion + * the region that has changed. + * @param changes + * the string representing the change. + * @param offset + * the offset of the change. + * @param lengthToReplace + * the length specified to be replaced. + */ + public RegionChangedEvent(IStructuredDocument document, Object originalRequester, IStructuredDocumentRegion structuredDocumentRegion, ITextRegion changedRegion, String changes, int offset, int lengthToReplace) { + super(document, originalRequester, changes, offset, lengthToReplace); + fStructuredDocumentRegion = structuredDocumentRegion; + fChangedRegion = changedRegion; + } + + + /** + * Returns the text region changed. + * + * @return the text region changed + */ + public ITextRegion getRegion() { + return fChangedRegion; + } + + + /** + * Returns the document region changed. + * + * @return the document region changed + */ + public IStructuredDocumentRegion getStructuredDocumentRegion() { + return fStructuredDocumentRegion; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/RegionsReplacedEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/RegionsReplacedEvent.java new file mode 100644 index 0000000..21a2e4f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/RegionsReplacedEvent.java
@@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +/** + * This event is used when a node's regions change, but the document region + * itself doesn't. This says nothing about the semantics of the document + * region, that may still have changed. Also, its assumed/required that all + * the regions are replaced (even those that may not have changed). + * + * @plannedfor 1.0 + */ +public class RegionsReplacedEvent extends StructuredDocumentEvent { + private ITextRegionList fNewRegions; + private ITextRegionList fOldRegions; + private IStructuredDocumentRegion fStructuredDocumentRegion; + + /** + * Creates an instance of the RegionsReplacedEvent. + * + * @param document - + * document being changed. + * @param originalRequester - + * requester of the change. + * @param structuredDocumentRegion - + * the region containing the change. + * @param oldRegions - + * the old Regions being replaced. + * @param newRegions - + * the new regions being added. + * @param changes - + * the String representing the change. + * @param offset - + * the offset of the change. + * @param lengthToReplace - + * the length of text to replace. + */ + public RegionsReplacedEvent(IStructuredDocument document, Object originalRequester, IStructuredDocumentRegion structuredDocumentRegion, ITextRegionList oldRegions, ITextRegionList newRegions, String changes, int offset, int lengthToReplace) { + super(document, originalRequester, changes, offset, lengthToReplace); + fStructuredDocumentRegion = structuredDocumentRegion; + fOldRegions = oldRegions; + fNewRegions = newRegions; + } + + /** + * Returns the new text regions. + * + * @return the new text regions. + */ + public ITextRegionList getNewRegions() { + return fNewRegions; + } + + /** + * Returns the old text regions. + * + * @return the old text regions. + */ + public ITextRegionList getOldRegions() { + return fOldRegions; + } + + /** + * Returns the structured document region. + * + * @return the structured document region. + */ + public IStructuredDocumentRegion getStructuredDocumentRegion() { + return fStructuredDocumentRegion; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/StructuredDocumentEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/StructuredDocumentEvent.java new file mode 100644 index 0000000..e3930ff --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/StructuredDocumentEvent.java
@@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + + +/** + * IStructuredDocument events are generated by the IStructuredDocument, after + * the IStructuredDocument acts on a request. Not intended to be instantiated, + * except by subclasses in infrastructure. Not intended to be subclassed by + * clients. + * + * @plannedfor 1.0 + */ +public abstract class StructuredDocumentEvent extends DocumentEvent { + private String fDeletedText; + private Object fOriginalRequester; + + /** + * There is no public null-arg version of this constructor. + */ + private StructuredDocumentEvent() { + super(); + } + + /** + * We assume (and require) that an IStructuredDocument's are always the + * source of StructuredDocument events. + * + * @param document - + * the document being changed + */ + private StructuredDocumentEvent(IStructuredDocument document) { + this(); + if (document == null) + throw new IllegalArgumentException("null source"); //$NON-NLS-1$ + fDocument = document; + fOriginalRequester = document; + } + + /** + * We assume (and require) that an IStructuredDocument's are always the + * source of StructuredDocument events. + * + * @param document - + * the document being changed. + * @param originalRequester - + * the original requester of the change. + */ + StructuredDocumentEvent(IStructuredDocument document, Object originalRequester) { + this(document); + fOriginalRequester = originalRequester; + } + + /** + * We assume (and require) that an IStructuredDocument's are always the + * source of StructuredDocument events. + * + * @param document - + * the document being changed. + * @param originalRequester - + * the requester of the change. + * @param changes - + * the String representing the new text + * @param offset - + * the offset of the change. + * @param lengthToReplace - + * the length of text to replace. + */ + StructuredDocumentEvent(IStructuredDocument document, Object originalRequester, String changes, int offset, int lengthToReplace) { + this(document); + fOriginalRequester = originalRequester; + fText = changes; + fOffset = offset; + fLength = lengthToReplace; + } + + /** + * Provides the text that is being deleted. + * + * @return the text that is being deleted, or null if none is being + * deleted. + */ + public String getDeletedText() { + return fDeletedText; + } + + /** + * This method returns the object that originally caused the event to + * fire. This is typically not the object that created the event (the + * IStructuredDocument) but instead the object that made a request to the + * IStructuredDocument. + * + * @return the object that made the request to the document + */ + public Object getOriginalRequester() { + return fOriginalRequester; + } + + /** + * This method is equivalent to 'getDocument' except it returns an object + * of the appropriate type (namely, a IStructuredDocument, instead of + * IDocument). + * + * @return IStructuredDocumnt - the document being changed + */ + public IStructuredDocument getStructuredDocument() { + // a safe case, since constructor can only be called with a + // IStructuredDocument + return (IStructuredDocument) fDocument; + } + + /** + * Not to be called by clients, only parsers and reparsers. (will + * eventually be moved to an SPI package). + * + * @param newDeletedText - + * the text that has been deleted. + */ + public void setDeletedText(String newDeletedText) { + fDeletedText = newDeletedText; + } + + /** + * for debugging only + * + * @deprecated - need to fix unit tests which depend on this exact format, + * then delete this + */ + public String toString() { + // return getClass().getName() + "[source=" + source + "]"; + return getClass().getName(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/StructuredDocumentRegionsReplacedEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/StructuredDocumentRegionsReplacedEvent.java new file mode 100644 index 0000000..8f08b88 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/events/StructuredDocumentRegionsReplacedEvent.java
@@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.events; + + + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; + +/** + * This event is used when a IStructuredDocumentRegion is deleted, or replaced + * with more than one IStructuredDocumentRegion, or when simply more than one + * IStructuredDocumentRegion changes. + * + * @plannedfor 1.0 + */ +public class StructuredDocumentRegionsReplacedEvent extends StructuredDocumentEvent { + + private IStructuredDocumentRegionList fNewStructuredDocumentRegions; + private IStructuredDocumentRegionList fOldStructuredDocumentRegions; + + private boolean fIsEntireDocumentReplaced; + + /** + * Creates an instance of StructuredDocumentRegionsReplacedEvent + * + * @param document - + * the document being changed. + * @param originalRequester - + * the requester of the change. + * @param oldStructuredDocumentRegions - + * the old document regions removed. + * @param newStructuredDocumentRegions - + * the new document regions added. + * @param changes - + * a string representing the text change. + * @param offset - + * the offset of the change. + * @param lengthToReplace - + * the length of text requested to be replaced. + */ + public StructuredDocumentRegionsReplacedEvent(IStructuredDocument document, Object originalRequester, IStructuredDocumentRegionList oldStructuredDocumentRegions, IStructuredDocumentRegionList newStructuredDocumentRegions, String changes, int offset, int lengthToReplace) { + super(document, originalRequester, changes, offset, lengthToReplace); + fOldStructuredDocumentRegions = oldStructuredDocumentRegions; + fNewStructuredDocumentRegions = newStructuredDocumentRegions; + } + + public StructuredDocumentRegionsReplacedEvent(IStructuredDocument document, Object originalRequester, IStructuredDocumentRegionList oldStructuredDocumentRegions, IStructuredDocumentRegionList newStructuredDocumentRegions, String changes, int offset, int lengthToReplace, boolean entireDocumentReplaced) { + this(document, originalRequester, oldStructuredDocumentRegions, newStructuredDocumentRegions, changes, offset, lengthToReplace); + fIsEntireDocumentReplaced = entireDocumentReplaced; + } + + /** + * Returns the new structured document regions. + * + * @return the new structured document regions. + */ + public IStructuredDocumentRegionList getNewStructuredDocumentRegions() { + return fNewStructuredDocumentRegions; + } + + /** + * Returns the old structured document regions. + * + * @return the old structured document regions. + */ + public IStructuredDocumentRegionList getOldStructuredDocumentRegions() { + return fOldStructuredDocumentRegions; + } + + public boolean isEntireDocumentReplaced() { + return fIsEntireDocumentReplaced; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/exceptions/ResourceAlreadyExists.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/exceptions/ResourceAlreadyExists.java new file mode 100644 index 0000000..79ff0e6 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/exceptions/ResourceAlreadyExists.java
@@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.exceptions; + + + +/** + * Indicates that a Resource which a model or document was expected to create + * already exists. + */ +public class ResourceAlreadyExists extends Exception { + + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + /** + * ResourceAlreadyExists constructor comment. + */ + public ResourceAlreadyExists() { + super(); + } + + /** + * ResourceAlreadyExists constructor comment. + * + * @param s + * java.lang.String + */ + public ResourceAlreadyExists(String s) { + super(s); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/exceptions/ResourceInUse.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/exceptions/ResourceInUse.java new file mode 100644 index 0000000..5ecbfad --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/exceptions/ResourceInUse.java
@@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.exceptions; + + + +/** + * Indicates that a model with a particular ID already exists + */ +public class ResourceInUse extends Exception { + + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + /** + * ResourceAlreadyExists constructor comment. + */ + public ResourceInUse() { + super(); + } + + /** + * ResourceAlreadyExists constructor comment. + * + * @param s + * java.lang.String + */ + public ResourceInUse(String s) { + super(s); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IModelManagerProposed.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IModelManagerProposed.java new file mode 100644 index 0000000..ace9420 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IModelManagerProposed.java
@@ -0,0 +1,338 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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.wst.sse.core.internal.provisional.model; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists; +import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +/** + * Responsible for managing IStructuredModels. + * + * This allows clients to share models, so they do not have to be re-created + * or passed around from one client to another. Clients should internally + * enforce that models are gotten and released from locations close to each + * other to prevent model leaks. + * + * There are three ways to get a model based on usage and responsibility: for + * 'MODIFY', just for 'SHARED', and 'UNSHARED'. Contrary to their names, a + * model can technically be modified, and all modifications directly affect + * the commonly shared version of the model. It is part of the API contract, + * however, that clients who get a model for SHARED do not modify it. The + * significance of the 'MODIFY' model is that the client is registering their + * interest in saving changes to the model. + * + * Clients can reference this interface, but should not implement. + * + * @see org.eclipse.wst.sse.core.StructuredModelManager + * @plannedfor 1.0 + */ +public interface IModelManagerProposed { + + /** + * AccessType is used internally as a JRE 1.4 compatible enumerated type. + * Not intended to be referenced by clients. + */ + static class AccessType { + private String fType; + + /** + * default access contructor we use to create our specific constants. + * + * @param type + */ + AccessType(String type) { + super(); + fType = type; + } + + /** + * For debug purposes only. + */ + public String toString() { + return "Model Access Type: " + fType; //$NON-NLS-1$ + } + } + + /** + * Constant to provide compile time safe parameter. <code>NOTSHARED</code>signifies + * the client intentially wants a model that is not shared with other + * clients. NOTSHARED models can not be saved. + */ + final AccessType NOTSHARED = new AccessType("NOTSHARED"); //$NON-NLS-1$ + + /** + * Constant to provide compile-time safe parameter. <code>SHARED</code>signifies + * the client is not intending to make changes and does not care whether + * the model accessed is saved. + */ + final AccessType SHARED = new AccessType("SHARED"); //$NON-NLS-1$ + + /** + * Constant to provide compile-time safe parameter. <code>MODIFY</code>signifies + * the client is intending to make changes and is responsible for saving + * changes (or not) if they are the last one holding MODIFY access to the + * model before it's released. + */ + final AccessType MODIFY = new AccessType("MODIFY"); //$NON-NLS-1$ + + /** + * copyModel is similar to a deep clone. The resulting model is shared, + * according to the value of ReadEditType. If a model already is being + * managed for 'newLocation' then a ResourceInUse exception is thrown, + * unless the ReadEditType is NOTSHARED, in which case the resulting model + * can not be saved. + * + * @param oldLocation + * @param newLocation + * @param type + * @return + * @throws ResourceInUse + * + * ISSUE: is this important enough to be API, or can clients solve + * themselves + */ + IStructuredModel copyModel(IPath oldLocation, IPath newLocation, AccessType type) throws ResourceInUse; + + /** + * createNewInstance is similar to clone, except the new instance has no + * text content. Note: this produces an UNSHARED model, for temporary use, + * that has the same characteristics as original model. If a true shared + * model is desired, use "copy". + * + * ISSUE: still needed? + * + * @param requester + * @param model + * @return + * @throws IOException + */ + public IStructuredModel createNewInstance(Object requester, IStructuredModel model) throws IOException; + + /** + * Factory method, since a proper IStructuredDocument must have a proper + * parser assigned. Note: its assume that IPath does not actually exist as + * a resource yet. If it does, ResourceAlreadyExists exception is thrown. + * If the resource does already exist, then createStructuredDocumentFor is + * the right API to use. + * + * ISSUE: do we want to support this via model manager, or else where? + * ISSUE: do we need two of these? What's legacy use case? + * + * @param location + * @param progressMonitor + * @return + * @throws ResourceAlreadyExists + * @throws IOException + * @throws CoreException + */ + IStructuredDocument createNewStructuredDocumentFor(IPath location, IProgressMonitor progressMonitor) throws ResourceAlreadyExists, IOException, CoreException; + + /** + * Factory method, since a proper IStructuredDocument must have a proper + * parser assigned. Note: clients should verify that the resource + * identified by the IPath exists before using this method. If this IFile + * does not exist, then createNewStructuredDocument is the correct API to + * use. + * + * ISSUE: do we want to support this via model manager, or else where? + * ISSUE: do we need two of these? What's legacy use case? + * + * @param location + * @param progressMonitor + * @return + * @throws IOException + * @throws CoreException + */ + IStructuredDocument createStructuredDocumentFor(IPath location, IProgressMonitor progressMonitor) throws IOException, CoreException; + + /** + * Note: callers of this method must still release the model when + * finished. Returns the model for this document if it already exists and + * is being shared. Returns null if this is not the case. The ReadEditType + * must be either MODIFY or SHARED. + * + * ISSUE: should we accept IDocument on parameter for future evolution, + * and constrain to StructuredDocuments at runtime? + * + * @param requester + * @param type + * @param document + * @return + */ + IStructuredModel getExistingModel(Object requester, AccessType type, IDocument document); + + /** + * Callers of this method must still release the model when finished. + * Returns the model for this location if it already exists and is being + * shared. Returns null if this is not the case. The ReadEditType must be + * either MODIFY or SHARED. + * + * @param requester + * @param type + * @param location + * @return + */ + public IStructuredModel getExistingModel(Object requester, AccessType type, IPath location); + + /** + * Returns the model that has the specified document as its structured + * document. + * + * @param requester + * @param type + * @param progressMonitor + * @param document + * @return + */ + public IStructuredModel getModel(Object requester, AccessType type, IProgressMonitor progressMonitor, IDocument document); + + /** + * Returns the model based on the content at the specified location. + * + * Note: if the ModelManager does not know how to create a model for + * such a file for content, null is return. + * ISSUE: should we throw some special, meaningful, checked + * exception instead? + * + * @param requester + * @param type + * @param progressMonitor + * @param location + * @return + * @throws IOException + * @throws CoreException + */ + public IStructuredModel getModel(Object requester, AccessType type, IProgressMonitor progressMonitor, IPath location) throws IOException, CoreException; + + /** + * This method will not create a new model if it already exists ... if + * force is false. The idea is that a client should call this method once + * with force set to false. If the exception is thrown, then prompt client + * if they want to overwrite. + * + * @param requester + * @param location + * @param force + * @param type + * @param progressMonitor + * @return + * @throws ResourceAlreadyExists + * @throws ResourceInUse + * @throws IOException + * @throws CoreException + */ + IStructuredModel getNewModel(Object requester, IPath location, boolean force, AccessType type, IProgressMonitor progressMonitor) throws ResourceAlreadyExists, ResourceInUse, IOException, CoreException; + + /** + * This function returns true if there are other references to the + * underlying model. + * + * @param location + * @return + */ + boolean isShared(IPath location); + + /** + * This function returns true if there are other references to the + * underlying model, shared in the specified way. The ReadEditType must be + * either MODIFY or SHARED. + * + * @param location + * @param type + * @return + */ + boolean isShared(IPath location, AccessType type); + + /** + * This method can be called when the content type of a model changes. Its + * assumed the contentType has already been changed, and this method uses + * the text of the old one, to repopulate the text of the new one. + * + * @param model + * @return + * @throws IOException + */ + IStructuredModel reinitialize(IStructuredModel model) throws IOException; + + + /** + * This is used by clients to signify that they are finished with a model + * and will no longer access it or any of its underlying data (such as its + * structured document). The ReadEditType must match what ever the client + * used in the corresponding 'get' method. + * + * This method must be called for every 'get'. Clients should use the + * try/finally pattern to ensure the release is called even if there is an + * unexpected exception. + * + * @param requester + * @param structuredModel + * @param type + */ + void releaseModel(Object requester, IStructuredModel structuredModel, AccessType type); + + /** + * Writes the underlying document to the IPath. + * + * ISSUE: we want to just "dump" contents to location, but need to spec. + * all the different cases of shared, etc. + * + * ?If to same location as 'get', then same instance of model, If to + * different location (esp. if shared) then need to leave old instance of + * model, create new model, and save to new location. ? + * + * Cases: IPath is null, write to IPath created with IPath specificed and + * equals IPath created with, write to IPath IPath specified and not + * equals IPath created with, dumps to new IPath, no change in current + * model state. + * + * ISSUE: think through 'normalization' cases + * + * + * @param structuredModel + * @param location - already normalized? + * @param progressMonitor + * @throws UnsupportedEncodingException + * @throws IOException + * @throws CoreException + * + * ISSUE: resource aleady exists? veto override + */ + void saveModel(IStructuredModel structuredModel, IPath location, IProgressMonitor progressMonitor) throws UnsupportedEncodingException, IOException, CoreException; + + /** + * Writes the underlying document to the IPath if the model is only shared + * for EDIT by one client. This is the recommended way for 'background + * jobs' to save models, in case the model is being shared by an editor, + * or other client that might desire user intervention to save a resource. + * + * @param structuredModel + * @param location - already normalized? + * @param progressMonitor + * @throws UnsupportedEncodingException + * @throws IOException + * @throws CoreException + * + * ISSUE: is locaiton needed in this case, or just use the one it was + * created with + */ + void saveModelIfNotShared(IStructuredModel structuredModel, IPath location, IProgressMonitor progressMonitor) throws UnsupportedEncodingException, IOException, CoreException; +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IModelStateListenerProposed.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IModelStateListenerProposed.java new file mode 100644 index 0000000..484fc70 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IModelStateListenerProposed.java
@@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.model; + + +/** + * Interface for those wanting to listen to a model's state changing. + * + * @plannedfor 1.0 + */ +public interface IModelStateListenerProposed { + + /** + * A model is about to be changed. The event object itself signifies which + * model, and any pertinent information. + */ + void modelAboutToBeChanged(IStructuredModelEvent event); + + /** + * Signals that the changes foretold by modelAboutToBeChanged have been + * made. A typical use might be to refresh, or to resume processing that + * was suspended as a result of modelAboutToBeChanged. + */ + void modelChanged(IStructuredModelEvent event); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/INodeAdapterFactoryManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/INodeAdapterFactoryManager.java new file mode 100644 index 0000000..6c2d165 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/INodeAdapterFactoryManager.java
@@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2005 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.wst.sse.core.internal.provisional.model; + +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; +/** + * Provides a means for clients to register IAdapterFactories for use + * by infrastructure when StructuredModels are created. + */ +public interface INodeAdapterFactoryManager { + + /** + * + * @param factory + * @param contentType + */ + void addAdapterFactory(INodeAdapterFactory factory, IContentType contentType); + + void removeAdapterFactory(INodeAdapterFactory factory, IContentType contentType); + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IStructuredModelEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IStructuredModelEvent.java new file mode 100644 index 0000000..73bc3be --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IStructuredModelEvent.java
@@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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.wst.sse.core.internal.provisional.model; + +public interface IStructuredModelEvent { + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IStructuredModelProposed.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IStructuredModelProposed.java new file mode 100644 index 0000000..7d984da --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/model/IStructuredModelProposed.java
@@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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.wst.sse.core.internal.provisional.model; + +import java.io.IOException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; +import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + + +/** + * IStructuredModel's are mainly interesting by their extensions and + * implementers. The main purposed of this abstraction it to provide a common + * way to manage models that have an associated structured documnet. + * + * @plannedfor 1.0 + * + */ +public interface IStructuredModelProposed extends IAdaptable { + + + /** + * This API allows clients to declare that they are about to make a + * "large" change to the model. This change might be in terms of content + * or it might be in terms of the model id or base location. + * + * Note that in the case of embedded calls, notification to listeners is + * sent only once. + * + * Note that the client who is making these changes has the responsibility + * to restore the model's state once finished with the changes. See + * getMemento and restoreState. + * + * The method isModelStateChanging can be used by a client to determine if + * the model is already in a change sequence. + * + * This method is a matched pair to changedModel, and must be called + * before changedModel. A client should never call changedModel without + * calling aboutToChangeModel first nor call aboutToChangeModel without + * calling changedModel later from the same Thread. + */ + void aboutToChangeModel(); + + void addModelStateListener(IModelStateListener listener); + + /** + * This API allows a client controlled way of notifying all ModelEvent + * listners that the model has been changed. This method is a matched pair + * to aboutToChangeModel, and must be called after aboutToChangeModel ... + * or some listeners could be left waiting indefinitely for the changed + * event. So, its suggested that changedModel always be in a finally + * clause. Likewise, a client should never call changedModel without + * calling aboutToChangeModel first. + * + * In the case of embedded calls, the notification is just sent once. + * + */ + void changedModel(); + + /** + * This is a client-defined value for what that client (and/or loader) + * considers the "base" of the structured model. Frequently the location + * is either a workspace root-relative path of a workspace resource or an + * absolute path in the local file system. + */ + IPath getLocation(); + + /** + * @return The associated content type identifier (String) for this model. + */ + IContentType getContentType() throws CoreException; + + /** + * + * @return The model's FactoryRegistry. A model is not valid without one. + */ + FactoryRegistry getFactoryRegistry(); + + /** + * Return the index region at offset. Returns null if there is no + * IndexedRegion that contains offset. + */ + IndexedRegion getIndexedRegion(int offset); + + /** + * ISSUE: do we want to provide this? How to ensure job/thread safety + * + * @return + */ + IndexedRegion[] getIndexedRegions(); + + /** + * Rreturns the IStructuredDocument that underlies this model + * + * @return + */ + IStructuredDocument getStructuredDocument(); + + /** + * + */ + boolean isDirty(); + + /** + * This method can be called to determine if the model is within a + * "aboutToChange" and "changed" sequence. + */ + public boolean isModelStateChanging(); + + /** + * + */ + boolean isNew(); + + boolean isReinitializationNeeded(); + + /** + * This is a combination of if the model is dirty and if the model is + * shared for write access. The last writer as the responsibility to be + * sure the user is prompted to save. + */ + public boolean isSaveNeeded(); + + /** + * newInstance is similar to clone, except that the newInstance contains + * no content. Its purpose is so clients can get a temporary, unmanaged, + * model of the same "type" as the original. Note: the client may still + * need to do some intialization of the model returned by newInstance, + * depending on desired use. For example, the only factories in the + * newInstance are those that would be normally be created for a model of + * the given contentType. Others are not copied automatically, and if + * desired, should be added by client. + */ + IStructuredModelProposed newInstance() throws IOException; + + void removeModelStateListener(IModelStateListener listener); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocument.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocument.java new file mode 100644 index 0000000..97d4ef6 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocument.java
@@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.text.IDocumentExtension; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; +import org.eclipse.wst.sse.core.internal.provisional.events.IModelAboutToBeChangedListener; +import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager; + + +/** + * A IStructuredDocument is a collection of StructuredDocumentRegions. It's + * often called "flat" because its contents by design do not contain much + * structural information beyond containment. Clients should not implement. + */ +public interface IStructuredDocument extends IEncodedDocument, IDocumentExtension, IAdaptable { + + void addDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener); + + /** + * The StructuredDocumentListeners and ModelChangedListeners are very + * similar. They both receive identical events. The difference is the + * timing. The "pure" StructuredDocumentListeners are notified after the + * structuredDocument has been changed, but before other, related models + * may have been changed such as the Structural Model. The Structural + * model is in fact itself a "pure" StructuredDocumentListner. The + * ModelChangedListeners can rest assured that all models and data have + * been updated from the change by the tiem they are notified. This is + * especially important for the text widget, for example, which may rely + * on both structuredDocument and structural model information. + */ + void addDocumentChangedListener(IStructuredDocumentListener listener); + + /** + * The StructuredDocumentListeners and ModelChangedListeners are very + * similar. They both receive identical events. The difference is the + * timing. The "pure" StructuredDocumentListeners are notified after the + * structuredDocument has been changed, but before other, related models + * may have been changed such as the Structural Model. The Structural + * model is in fact itself a "pure" StructuredDocumentListner. The + * ModelChangedListeners can rest assured that all models and data have + * been updated from the change by the tiem they are notified. This is + * especially important for the text widget, for example, which may rely + * on both structuredDocument and structural model information. + */ + void addDocumentChangingListener(IStructuredDocumentListener listener); + + /** + * this API ensures that any portion of the document within startOff to + * length is not readonly (that is, that its editable). Note that if the + * range overlaps with other readonly regions, those other readonly + * regions will be adjusted. + * + * @param startOffset + * @param length + */ + void clearReadOnly(int startOffset, int length); + + /** + * returns true if any portion of startOffset to length is readonly + * + * @param startOffset + * @param length + * @return + */ + boolean containsReadOnly(int startOffset, int length); + + /** + * This method is to remember info about the encoding When the resource + * was last loaded or saved. Note: it is not kept "current", that is, can + * not be depended on blindly to reflect what encoding to use. For that, + * must go through the normal rules expressed in Loaders and Dumpers. + */ + + EncodingMemento getEncodingMemento(); + + org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion getFirstStructuredDocumentRegion(); + + org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion getLastStructuredDocumentRegion(); + + /** + * This can be considered the preferred delimiter. + */ + public String getLineDelimiter(); + + int getLineOfOffset(int offset); // throws SourceEditingException; + + /** + * The parser is now required on constructor, so there are occasions it + * needs to be retrieved, such as to be initialized by EmbeddedContentType + */ + RegionParser getParser(); + + /** + * @deprecated use getStructuredDocumentRegions() + * @return + */ + IStructuredDocumentRegionList getRegionList(); + + /** + * Returns the <code>IStructuredDocumentRegion</code> at the given character offset. + * @param offset + * @return the <code>IStructuredDocumentRegion</code> at the given character offset. + */ + IStructuredDocumentRegion getRegionAtCharacterOffset(int offset); + + /** + * Returns <code>IStructuredDocumentRegion</code>s in the specified range. + * @param offset + * @param length + * @return <code>IStructuredDocumentRegion</code>s in the specified range. + */ + IStructuredDocumentRegion[] getStructuredDocumentRegions(int offset, int length); + + /** + * Returns all <code>IStructuredDocumentRegion</code>s in the document. + * @return all <code>IStructuredDocumentRegion</code>s in the document. + */ + IStructuredDocumentRegion[] getStructuredDocumentRegions(); + + /** + * Note: this method was made public, and part of the interface, for + * easier testing. Clients normally never manipulate the reparser directly + * (nor should they need to). + */ + IStructuredTextReParser getReParser(); + + String getText(); + + IStructuredTextUndoManager getUndoManager(); + + /** + * causes that portion of the document from startOffset to length to be + * marked as readonly. Note that if this range overlaps with some other + * region with is readonly, the regions are effectivly combined. + * + * @param startOffset + * @param length + */ + void makeReadOnly(int startOffset, int length); + + /** + * newInstance is similar to clone, except it contains no data. One + * important thing to duplicate is the parser, with the parser correctly + * "cloned", including its tokeninzer, block tags, etc. + * + * NOTE: even after obtaining a 'newInstance' the client may have to do + * some initialization, for example, it may need to add its own model + * listeners. Or, as another example, if the IStructuredDocument has a + * parser of type StructuredDocumentRegionParser, then the client may need + * to add its own StructuredDocumentRegionHandler to that parser, if it is + * in fact needed. + */ + IStructuredDocument newInstance(); + + void removeDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener); + + void removeDocumentChangedListener(IStructuredDocumentListener listener); + + void removeDocumentChangingListener(IStructuredDocumentListener listener); + + /** + * One of the APIs to manipulate the IStructuredDocument. + * + * replaceText replaces the text from oldStart to oldEnd with the new text + * found in the requestedChange string. If oldStart and oldEnd are equal, + * it is an insertion request. If requestedChange is null (or empty) it is + * a delete request. Otherwise it is a replace request. + */ + StructuredDocumentEvent replaceText(Object source, int oldStart, int replacementLength, String requestedChange); + + /** + * Note, same as replaceText API, but will allow readonly areas to be + * replaced. This should seldom be called with a value of "true" for + * ignoreReadOnlySetting. One case where its ok is with undo operations + * (since, presumably, if user just did something that happended to + * involve some inserting readonly text, they should normally be allowed + * to still undo that operation. Otherwise, I can't think of a single + * example, unless its to give the user a choice, e.g. "you are about to + * overwrite read only portions, do you want to continue". + */ + StructuredDocumentEvent replaceText(Object source, int oldStart, int replacementLength, String requestedChange, boolean ignoreReadOnlySetting); + + /** + * This method is to remember info about the encoding When the resource + * was last loaded or saved. Note: it is not kept "current", that is, can + * not be depended on blindly to reflect what encoding to use. For that, + * must go through the normal rules expressed in Loaders and Dumpers. + */ + void setEncodingMemento(EncodingMemento encodingMemento); + + public void setLineDelimiter(String delimiter); + + /** + * One of the APIs to manipulate the IStructuredDocument in terms of Text. + * + * The setText method replaces all text in the model. + */ + StructuredDocumentEvent setText(Object requester, String allText); + + void setUndoManager(IStructuredTextUndoManager undoManager); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocumentRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocumentRegion.java new file mode 100644 index 0000000..8b03b2a --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocumentRegion.java
@@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +/** + * A ITextRegionCollection is a collection of ITextRegions. It is a structural + * unit, but a minimal one. For example, in might consist of a "start tag" but + * not a whole XML element. + */ +public interface IStructuredDocumentRegion extends ITextRegionCollection { + + /** + * Adds a text region to the end of the collection of regions contained by + * this region. It is the parsers responsibility to make sure its a + * correct region (that is, its start offset is one greater than previous + * regions end offset) + * + * For use by parsers and reparsers only. + */ + void addRegion(ITextRegion aRegion); + + /** + * Returns the structured document region that follows this one or null if + * at end of document. + * + * @return the structured document region that follows this one. + * + * ISSUE: for thread safety, this should be more restrictive. + */ + IStructuredDocumentRegion getNext(); + + /** + * Returns this regions parent document. + * + * @return this regions parent document. + */ + IStructuredDocument getParentDocument(); + + /** + * Returns the structured document region that preceeds this one or null + * if at beginning of document. + * + * @return the structured document region that follows this one. + * + * ISSUE: for thread safety, this should be more restrictive. + */ + IStructuredDocumentRegion getPrevious(); + + /** + * Returns true if this document has been deleted, and is no longer part + * of the actual structured document. This field can be used in + * multi-threaded operations, which may retrieve a long list of regions + * and be iterating through them at the same time the document is + * modified, and regions deleted. + * + * @return true if this region is no longer part of document. + */ + boolean isDeleted(); + + /** + * Returns true if this structured document region was ended "naturally" + * by syntactic rules, or if simply assumed to end so another could be + * started. + * + * @return true if region has syntactic end. + */ + boolean isEnded(); + + /** + * Tests is region is equal to this one, ignoring position offsets of + * shift. + * + * For use by parsers and reparsers only. + * + * @param region + * @param shift + * @return + */ + boolean sameAs(IStructuredDocumentRegion region, int shift); + + /** + * Tests if <code>oldRegion</code> is same as <code>newRegion</code>, + * ignoring position offsets of <code>shift</code>. + * + * ISSUE: which document region are old and new in? + * + * For use by parsers and reparsers only. + * + * @param oldRegion + * @param documentRegion + * @param newRegion + * @param shift + * @return + */ + boolean sameAs(ITextRegion oldRegion, IStructuredDocumentRegion documentRegion, ITextRegion newRegion, int shift); + + /** + * Set to true if/when this region is removed from a document, during the + * process of re-parsing. + * + * For use by parsers and reparsers only. + * + * @param deleted + */ + void setDeleted(boolean deleted); + + /** + * Set to true by parser/reparser if region deemed to end syntactically. + * + * For use by parsers and reparsers only. + * + * @param hasEnd + */ + void setEnded(boolean hasEnd); + + /** + * Sets length of region. + * + * For use by parsers and reparsers only. + */ + void setLength(int newLength); + + /** + * Assigns pointer to next region, or null if last region. + * + * For use by parsers and reparsers only. + */ + void setNext(IStructuredDocumentRegion newNext); + + /** + * Assigns parent documnet. + * + * For use by parsers and reparsers only. + */ + void setParentDocument(IStructuredDocument document); + + /** + * Assigns pointer to previous region, or null if first region. + * + * For use by parsers and reparsers only. + */ + void setPrevious(IStructuredDocumentRegion newPrevious); + + /** + * Sets start offset of region, relative to beginning of document. + * + * For use by parsers and reparsers only. + */ + void setStart(int newStart); + + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocumentRegionList.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocumentRegionList.java new file mode 100644 index 0000000..734431e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredDocumentRegionList.java
@@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +import java.util.Enumeration; + +/** + * This is a class used to provide a list of StructuredDocumentRegions, so the + * implementation of how the list is formed can be hidden (encapsulated by + * this class). + * + * ISSUE: should extend ITextRegionList instead? + * + * @plannedfor 1.0 + * + */ +public interface IStructuredDocumentRegionList { + + /** + * Returns enumeration. + * + * @return enumeration. + */ + Enumeration elements(); + + /** + * Returns size of list. + * + * @return size of list. + */ + int getLength(); + + /** + * Returns the structured document region at index i. + * + * @param i + * index of region to return + * @return the region at specified offset. + */ + IStructuredDocumentRegion item(int i); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredPartitionTypes.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredPartitionTypes.java new file mode 100644 index 0000000..17c2019 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredPartitionTypes.java
@@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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.wst.sse.core.internal.provisional.text; + +/** + * This interface is not intended to be implemented. + * It defines the partitioning for StructuredDocuments. + * Clients should reference the partition type Strings defined here directly. + * + * @deprecated use org.eclipse.wst.sse.core.internal.provisional.text.IStructuredPartitions + */ +public interface IStructuredPartitionTypes { + + String DEFAULT_PARTITION = "org.eclipse.wst.sse.ST_DEFAULT"; //$NON-NLS-1$ + String UNKNOWN_PARTITION = "org.eclipse.wst.sse.UNKNOWN_PARTITION_TYPE"; //$NON-NLS-1$ +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredPartitioning.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredPartitioning.java new file mode 100644 index 0000000..f7e940c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredPartitioning.java
@@ -0,0 +1,12 @@ +package org.eclipse.wst.sse.core.internal.provisional.text; + +/** + * Identifies the way that Documents are partitioned. + * + * @plannedfor 1.0 + */ +public interface IStructuredPartitioning { + + /** String to identify default partitioning*/ + String DEFAULT_STRUCTURED_PARTITIONING = "org.eclipse.wst.sse.core.default_structured_text_partitioning"; //$NON-NLS-1$ +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredTextPartitioner.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredTextPartitioner.java new file mode 100644 index 0000000..9e53f8f --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredTextPartitioner.java
@@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.wst.sse.core.internal.text.rules.IStructuredTypedRegion; + + +/** + * A partitioner interface required for handling the embedded content type + * properly. + * + * ISSUE: need to investigate necessity of these methods + * + * @plannedfor 1.0 + */ + +public interface IStructuredTextPartitioner extends IDocumentPartitioner { + + /** + * Used by JSP partitioner to ensure that the partitioner of the + * embedded content type gets to create the partition in case the specific + * classes are important. + * + * ISSUE: investigate if we really need this... + */ + IStructuredTypedRegion createPartition(int offset, int length, String partitionType); + + /** + * Returns the Default partition type for this partitioner. + * <p> + * eg: + * <br><code>org.eclipse.wst.xml.core.text.IXMLPartitions.XML_DEFAULT</code> + * <br><code>org.eclipse.wst.html.core.text.IHTMLPartitions.HTML_DEFAULT</code> + * <br><code>org.eclipse.wst.jsp.core.text.IJSPPartitions.JSP_DEFAULT</code> + * </p> + * @see org.eclipse.wst.sse.core.text.IStructuredPartitions + * @see org.eclipse.wst.xml.core.text.IXMLPartitions + * @see org.eclipse.wst.html.core.text.IHTMLPartitions + * @see org.eclipse.wst.jsp.core.text.IJSPPartitions + * + * @return the Default partition type for this partitioner. + */ + String getDefaultPartitionType(); + + /** + * Returns the particular partition type for the given region/offset. + * <p> + * eg: + * <br><code>org.eclipse.wst.xml.core.text.IXMLPartitions.XML_DEFAULT</code> + * <br><code>org.eclipse.wst.html.core.text.IHTMLPartitions.HTML_DEFAULT</code> + * <br><code>org.eclipse.wst.jsp.core.text.IJSPPartitions.JSP_DEFAULT</code> + * </p> + * + * @param region of the IStructuredDocument + * @param offset in the IStructuredDoucment + * @return the particular partition type for the given region/offset. + */ + String getPartitionType(ITextRegion region, int offset); + + /** + * Returns the partition type String of the IStructuredDocumentRegion + * between the 2 region parameters. + * Useful for finding the partition type of a 0 length region. + * eg. + * <pre> + * <script type="text/javascript">|</script> + * </pre> + * @param previousNode + * @param nextNode + * @return the partition type of the node between previousNode and nextNode + * @deprecated move to IDocumentPartitionerExtension2 -> + * String getContentType(int offset, boolean preferOpenPartitions); + */ + String getPartitionTypeBetween(IStructuredDocumentRegion previousNode, IStructuredDocumentRegion nextNode); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredTextReParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredTextReParser.java new file mode 100644 index 0000000..5acdfa1 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/IStructuredTextReParser.java
@@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + + +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; + +/** + * IStructuredTextReParser describes the characteristics and responsibilities + * for reparsing a structured document. + * + * @plannedfor 1.0 + */ +public interface IStructuredTextReParser { + + /** + * Begins the process of reparsing, by providing the information needed + * for the reparse. The reparse is actually performed when the reparse + * method is called. Will through an IllegalStateException if document as + * not be set. + * + * @param requester + * @param start + * @param lengthToReplace + * @param changes + */ + void initialize(Object requester, int start, int lengthToReplace, String changes); + + /** + * This method is provided to enable multiple threads to reparse a + * document. This is needed since the intialize method sets state + * variables that must be "in sync" with the structuredDocument. + */ + public boolean isParsing(); + + /** + * Returns a new instance of this reparser, used when cloning documents. + * + * @return a new instance of this reparser. + */ + public IStructuredTextReParser newInstance(); + + /** + * An entry point for reparsing. It needs to calculates the dirty start + * and dirty end in terms of structured document regions based on the + * start point and length of the changes, which are provided by the + * initialize method. Will through an IllegalStateException if document as + * not be set. + * + */ + public StructuredDocumentEvent reparse(); + + + + /** + * The reparser is initialized with its document, either in or shortly + * after its constructed is called. + * + * @param structuredDocument + */ + public void setStructuredDocument(IStructuredDocument newStructuredDocument); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegion.java new file mode 100644 index 0000000..b1281ab --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegion.java
@@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; + +/** + * A simple description of a bit of text (technically, a bit of a text buffer) + * that has a "type" associated with it. For example, for the XML text + * "<IMG>", the ' <' might form a region of type "open bracket" where as + * the text "IMG" might form a region of type "tag name". + * + * Note that there are three positions associated with a region, the start, + * the end, and the end of the text. The end of the region should always be + * greater than or equal to the end of the text, because the end of the text + * simply includes any white space that might follow the non-whitespace + * portion of the region. This whitespace is assumed to be ignorable except + * for reasons of maintaining it in the original document for formatting, + * appearance, etc. + * + * Follows the Prime Directives: + * + * getEnd() == getStart() + getLength() + * + * getTextEnd() == getStart() + getTextLength(); + * + * Event though 'end' and 'length' are redundant (given start), both methods + * are provided, since some parsers/implementations favor one or the other for + * efficiency. + * + * @plannedfor 1.0 + */ +public interface ITextRegion { + + /** + * Changes length of region. May be less than, equal to, or greater than + * zero. It may not, however, cause the length of the region to be less + * than or equal to zero, or an illegal argument acception may be thrown + * at runtime. + * + * For use by parsers and reparsers only. + */ + void adjustLength(int i); + + /** + * Changes start offset of region. May be less than, equal to, or greater + * than zero. It may not, however, cause the offset of the region to be + * less than zero, or an illegal argument acception may be thrown at + * runtime. + * + * For use by parsers and reparsers only. + */ + void adjustStart(int i); + + /** + * Changes text length of region. + * + * May be less than, equal to, or greater than zero. It may not, however, + * cause the text length of the region to be greater than the length of a + * region, or an illegal argument acception may be thrown at runtime. + * + * For use by parsers and reparsers only. + */ + void adjustTextLength(int i); + + /** + * Makes this regions start, length, and text length equal to the + * paremter's start, length, and text length. + * + * @param region + */ + void equatePositions(ITextRegion region); + + /** + * Returns the end offset of this region. Whether is relative to the + * document, or another region depends on the subtype. + * + * Follows the Prime Directive: getEnd() == getStart() + getLength() + * + * @return the end offset of this region. + */ + int getEnd(); + + /** + * Returns the length of the region. + * + * Follows the Prime Directive: getEnd() == getStart() + getLength() + * + * @return the length of the region. + */ + int getLength(); + + /** + * Returns the start of the region. Whether is relative to the document, + * or another region depends on the subtype. + * + * Follows the Prime Directive: getEnd() == getStart() + getLength() + * + * @return the start of the region. + */ + int getStart(); + + /** + * Returns the end offset of the text of this region. + * + * In some implementations, the "end" of the region (accessible via + * getEnd()) also contains any and all white space that may or may not be + * present after the "token" (read: relevant) part of the region. This + * method, getTextEnd(), is specific for the "token" part of the region, + * without the whitespace. + * + * @return the end offset of the text of this region. + */ + int getTextEnd(); + + /** + * Returns the length of the text of this region. + * + * The text length is equal to length if there is no white space at the + * end of a region. Otherwise it is smaller than length. + * + * @return the length of the text of this region. + */ + int getTextLength(); + + /** + * Returns the type of this region. + * + * @see regiontypes, structureddocumentregiontypes + * @return the type of this region. + */ + String getType(); + + /** + * Allows the region itself to participate in reparsing process. + * + * The region itself can decide if it can determine the appropriate event + * to return, based on the requested change. If it can not, this method + * must return null, so a "higher level" reparsing process will be given + * the oppurtunity to decide. If it returns an Event, that's it, not other + * reparsing process will get an oppurtunity to reparse. + * + * For use by parsers and reparsers only. + * + */ + StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionCollection.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionCollection.java new file mode 100644 index 0000000..08890ae --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionCollection.java
@@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +/** + * ITextRegionCollection, as its name implies, is a collection of + * ITextRegions. + * + * @plannedfor 1.0 + */ +public interface ITextRegionCollection extends ITextRegion { + + /** + * Used to determine if a region contains a particular offset, where + * offset is relative to the beginning of a document. + * + * @param offset + * @return true if offset is greater than or equal to regions start and + * less than its end offset. + */ + boolean containsOffset(int offset); + + /** + * Used to determine if a region contains a particular offset. + * + * ISSUE: I need to figure out what this is really for! (that is, how to + * describe it, or if still needed). + * + * @param offset + * @return true if offset is greater than or equal to regions start and + * less than its end offset. + */ + boolean containsOffset(ITextRegion region, int offset); + + /** + * Returns the end offset of this region, relative to beginning of + * document. + * + * For some subtypes, but not all, it is equivilent to getEnd(). + * + * @return the end offset of this region. + */ + int getEndOffset(); + + /** + * Returns the end offset, relative to the beginning of the document of + * the contained region. + * + * @param containedRegion + * @return the end offset of the contained region. + */ + int getEndOffset(ITextRegion containedRegion); + + /** + * Returns the first region of those contained by this region collection. + * + * @return the first region. + */ + ITextRegion getFirstRegion(); + + /** + * Returns the full text of this region, including whitespace. + * + * @return the full text of this region, including whitespace. + */ + String getFullText(); + + /** + * Returns the full text of the contained region, including whitespace. + * + * @return the full text of the contained region, including whitespace. + */ + String getFullText(ITextRegion containedRegion); + + /** + * Returns the last region of those contained by this region collection. + * + * @return the last region. + */ + ITextRegion getLastRegion(); + + /** + * Returns the number of regions contained by this region. + * + * @return the number of regions contained by this region. + */ + int getNumberOfRegions(); + + /** + * Returns the region that contains offset. In the case of "nested" + * regions, returns the top most region. + * + * @param offset + * relative to beginning of document. + * @return the region that contains offset. In the case of "nested" + * regions, returns the top most region. + */ + ITextRegion getRegionAtCharacterOffset(int offset); + + /** + * Returns the regions contained by this region. + * + * Note: no assumptions should be made about the object identity of the + * regions returned. Put another way, due to memory use optimizations, + * even if the underlying text has not changed, the regions may or may not + * be the same ones returned from one call to the next. + * + * @return the regions contained by this region. + */ + ITextRegionList getRegions(); + + /** + * Returns the start offset of this region, relative to the beginning of + * the document. + * + * @return the start offset of this region + */ + int getStartOffset(); + + /** + * Returns the start offset of the contained region, relative to the + * beginning of the document. + * + * @return the start offset of the contained region + */ + int getStartOffset(ITextRegion containedRegion); + + /** + * Returns the text of this region, not including white space. + * + * @return the text of this region, not including white space. + */ + String getText(); + + /** + * Returns the text of the contained region, not including white space. + * + * @return the text of the contained region, not including white space. + */ + String getText(ITextRegion containedRegion); + + /** + * Returns the end offset of the text of this region, not including white + * space. + * + * @return the end offset of the text of this region, not including white + * space. + */ + int getTextEndOffset(); + + /** + * Returns the end offset of the text of the contained region, not + * including white space. + * + * @return the end offset of the text of the contained region, not + * including white space. + */ + int getTextEndOffset(ITextRegion containedRegion); + + /** + * Assigns the collection contained in this region. + * + * For use by parsers and reparsers only. + * + * @param containedRegions + */ + void setRegions(ITextRegionList containedRegions); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionContainer.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionContainer.java new file mode 100644 index 0000000..654ca81 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionContainer.java
@@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +/** + * ITextRegionContainer contains other regions, like a ITextRegionCollection + * but is itself a region in an ITextRegionCollection, so its "parent" region + * is maintained. + * + * @plannedfor 1.0 + */ +public interface ITextRegionContainer extends ITextRegionCollection { + + /** + * Returns the parent region. + * + * @return the parent region. + */ + ITextRegionCollection getParent(); + + /** + * Sets the parent region. + * + * For use by parsers and reparsers only. + * + * @param parent + * the ITextRegionCollection this region is contained in. + */ + void setParent(ITextRegionCollection parent); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionList.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionList.java new file mode 100644 index 0000000..594d3e8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/provisional/text/ITextRegionList.java
@@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.provisional.text; + +import java.util.Iterator; + +/** + * ITextRegionList is to provide a list of regions. It can be used so clients + * do not need to be aware of underlying implementation. + */ +public interface ITextRegionList { + + /** + * Adds region to the list. + * + * For use by parsers and reparsers only, while list is being created. + * + * @param region + * @return + */ + public boolean add(ITextRegion region); + + /** + * Adds new regions to the list. + * + * For use by parsers and reparsers only, while list is being created. + * + * @param insertPos + * @param newRegions + * @return whether the contents of this list were modified + */ + public boolean addAll(int insertPos, ITextRegionList newRegions); + + /** + * Removes all regions from the list. + * + * For use by parsers and reparsers only, while list is being created. + * + */ + public void clear(); + + + /** + * Returns the region at <code>index</code>, where 0 is first one in + * the list. Throws an <code>ArrayIndexOutOfBoundsException</code> if + * list is empty, or if index is out of range. + * + * @param index + * @return + */ + public ITextRegion get(int index); + + /** + * Returns the index of <code>region</code> or -1 if <code>region</code> + * is not in the list. + * + * @param region + * @return + */ + public int indexOf(ITextRegion region); + + /** + * Returns true if list has no regions. + * + * @return true if list has no regions. + */ + public boolean isEmpty(); + + + /** + * Returns an iterator for this list. + * + * @return an iterator for this list. + */ + public Iterator iterator(); + + /** + * Removes the region at index. + * + * For use by parsers and reparsers only, while list is being created. + * + */ + public ITextRegion remove(int index); + + /** + * Removes the region. + * + * For use by parsers and reparsers only, while list is being created. + * + */ + public void remove(ITextRegion region); + + + /** + * Removes all regionList from this list. + * + * For use by parsers and reparsers only, while list is being created. + * + */ + public void removeAll(ITextRegionList regionList); + + /** + * Returns the size of the list. + * + * @return the size of the list. + */ + public int size(); + + + /** + * Creates and returns the regions in an array. No assumptions should be + * made if the regions in the array are clones are same instance of + * original region. + * + * ISSUE: do we need to specify if cloned copies or not? + * + * @return an array of regions. + */ + public ITextRegion[] toArray(); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocument.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocument.java new file mode 100644 index 0000000..c4821c1 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocument.java
@@ -0,0 +1,2979 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Jesper Steen Møller - initial IDocumentExtension4 support - #102822 + * (see also #239115) + * David Carver (Intalio) - bug 300434 - Make inner classes static where possible + * David Carver (Intalio) - bug 300443 - some constants aren't static final + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPartitioningException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.DefaultLineTracker; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.DocumentPartitioningChangedEvent; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionEvent; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.FindReplaceDocumentAdapter; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension; +import org.eclipse.jface.text.IDocumentExtension3; +import org.eclipse.jface.text.IDocumentExtension4; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.jface.text.IDocumentPartitionerExtension; +import org.eclipse.jface.text.IDocumentPartitionerExtension2; +import org.eclipse.jface.text.IDocumentPartitionerExtension3; +import org.eclipse.jface.text.IDocumentPartitioningListener; +import org.eclipse.jface.text.IDocumentPartitioningListenerExtension; +import org.eclipse.jface.text.IDocumentPartitioningListenerExtension2; +import org.eclipse.jface.text.IDocumentRewriteSessionListener; +import org.eclipse.jface.text.ILineTracker; +import org.eclipse.jface.text.ILineTrackerExtension; +import org.eclipse.jface.text.IPositionUpdater; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextStore; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.TypedRegion; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.document.StructuredDocumentFactory; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.provisional.events.AboutToBeChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.IModelAboutToBeChangedListener; +import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredPartitioning; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextReParser; +import org.eclipse.wst.sse.core.internal.text.rules.StructuredTextPartitioner; +import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager; +import org.eclipse.wst.sse.core.internal.undo.StructuredTextUndoManager; +import org.eclipse.wst.sse.core.internal.util.Assert; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.internal.util.Utilities; + + +/** + * The standard implementation of structured document. + */ +public class BasicStructuredDocument implements IStructuredDocument, IDocumentExtension, IDocumentExtension3, IDocumentExtension4, CharSequence, IRegionComparible { + + /** + * This ThreadLocal construct is used so each thread can maintain its only + * pointer to the double linked list that manages the documents regions. + * The only thing we "gaurd" for is that a previously cached region has + * been deleted. + * + * The object that is kept in the thread local's map, is just a pointer to + * an array position. That's because the object there needs to be "free" + * from references to other objects, or it will not be garbage collected. + */ + private class CurrentDocumentRegionCache { + // I'm assuming for now there would never be so many threads that + // this arrayList needs to be bounded, or 'cleaned up'. + // this assumption should be tested in practice and long running + // jobs -- found not to be a good assumption. See below. + private List cachedRegionPositionArray = Collections.synchronizedList(new ArrayList()); + private final boolean DEBUG = false; + private static final int MAX_SIZE = 50; + + + private ThreadLocal threadLocalCachePosition = new ThreadLocal(); + + IStructuredDocumentRegion get() { + IStructuredDocumentRegion region = null; + int pos = getThreadLocalPosition(); + try { + region = (IStructuredDocumentRegion) cachedRegionPositionArray.get(pos); + } + catch (IndexOutOfBoundsException e) { + // even though the cachedRegionPosition is synchronized, + // that just means each access is syncronized, its + // still possible for another thread to cause it to + // be cleared, after this thread gets it position. + // So, if that happens, all we can do is reset to beginning. + // This should be extremely rare (in other words, probably + // not worth using synchronized blocks + // to access cachedRegionPositionArray. + reinitThreadLocalPosition(); + resetToInitialState(); + } + if (region == null) { + region = resetToInitialState(); + } + else + // region not null + if (region.isDeleted()) { + region = resetToInitialState(); + } + return region; + } + + private int getThreadLocalPosition() { + Object threadLocalObject = threadLocalCachePosition.get(); + int pos = -1; + if (threadLocalObject == null) { + + pos = reinitThreadLocalPosition(); + } + else { + pos = ((Integer) threadLocalObject).intValue(); + } + return pos; + } + + /** + * @return + */ + private int reinitThreadLocalPosition() { + Integer position; + int pos; + // TODO_future: think of a better solution that doesn't + // require this kludge. This is especially required because + // some infrasture, such as reconciler, actually null out + // their thread object and recreate it, 500 msecs later + // (approximately). + // Note: the likely solution in future is to clear after every + // heavy use of getCachedRegion, such as in creating node + // lists, or reparsing or partioning. + if (cachedRegionPositionArray.size() > MAX_SIZE) { + cachedRegionPositionArray.clear(); + if (DEBUG) { + System.out.println("cachedRegionPositionArray cleared at size " + MAX_SIZE); //$NON-NLS-1$ + } + } + position = new Integer(cachedRegionPositionArray.size()); + threadLocalCachePosition.set(position); + cachedRegionPositionArray.add(position.intValue(), null); + pos = position.intValue(); + return pos; + } + + private IStructuredDocumentRegion resetToInitialState() { + IStructuredDocumentRegion region; + region = getFirstStructuredDocumentRegion(); + set(region); + return region; + } + + void set(IStructuredDocumentRegion region) { + try { + int pos = getThreadLocalPosition(); + cachedRegionPositionArray.set(pos, region); + } + catch (IndexOutOfBoundsException e) { + // even though the cachedRegionPosition is synchronized, + // that just means each access is syncronized, its + // still possible for another thread to cause it to + // be cleared, after this thread gets it position. + // So, if that happens, all we can do is reset to beginning. + // This should be extremely rare (in other words, probably + // not worth using synchronized blocks + // to access cachedRegionPositionArray. + reinitThreadLocalPosition(); + resetToInitialState(); + } + } + } + + /** + * This NullDocumentEvent is used to complete the "aboutToChange" and + * "changed" cycle, when in fact the original change is no longer valid. + * The only known (valid) case of this is when a model re-initialize takes + * place, which causes setText to be called in the middle of some previous + * change. [This architecture will be improved in future]. + */ + public class NullDocumentEvent extends DocumentEvent { + public NullDocumentEvent() { + this(BasicStructuredDocument.this, 0, 0, ""); //$NON-NLS-1$ + } + + private NullDocumentEvent(IDocument doc, int offset, int length, String text) { + super(doc, offset, length, text); + } + } + + static class RegisteredReplace { + /** The owner of this replace operation. */ + IDocumentListener fOwner; + /** The replace operation */ + IDocumentExtension.IReplace fReplace; + + /** + * Creates a new bundle object. + * + * @param owner + * the document listener owning the replace operation + * @param replace + * the replace operation + */ + RegisteredReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) { + fOwner = owner; + fReplace = replace; + } + } + + /** + * these control variable isn't mark as 'final' since there's some unit + * tests that manipulate it. For final product, it should be. + */ + + private static boolean USE_LOCAL_THREAD = true; + + /** + * purely for debugging/performance measurements In practice, would always + * be 'true'. (and should never be called by called by clients). Its not + * 'final' or private just so it can be varied during + * debugging/performance measurement runs. + * + * @param use_local_thread + */ + public static void setUSE_LOCAL_THREAD(final boolean use_local_thread) { + USE_LOCAL_THREAD = use_local_thread; + } + + private IStructuredDocumentRegion cachedDocumentRegion; + private EncodingMemento encodingMemento; + private boolean fAcceptPostNotificationReplaces = true; + private CurrentDocumentRegionCache fCurrentDocumentRegionCache; + private DocumentEvent fDocumentEvent; + private IDocumentListener[] fDocumentListeners; + + /** + * The registered document partitioners. + */ + private Map fDocumentPartitioners; + /** The registered document partitioning listeners */ + private List fDocumentPartitioningListeners; + private IStructuredDocumentRegion firstDocumentRegion; + private RegionParser fParser; + private GenericPositionManager fPositionManager; + private List fPostNotificationChanges; + private IDocumentListener[] fPrenotifiedDocumentListeners; + private int fReentranceCount = 0; + private IStructuredTextReParser fReParser; + private int fStoppedCount = 0; + + private ITextStore fStore; + private Object[] fStructuredDocumentAboutToChangeListeners; + private Object[] fStructuredDocumentChangedListeners; + private Object[] fStructuredDocumentChangingListeners; + + private List fDocumentRewriteSessionListeners; + + private ILineTracker fTracker; + private IStructuredTextUndoManager fUndoManager; + private IStructuredDocumentRegion lastDocumentRegion; + + private byte[] listenerLock = new byte[0]; + private NullDocumentEvent NULL_DOCUMENT_EVENT; + + /** + * Theoretically, a document can contain mixed line delimiters, but the + * user's preference is usually to be internally consistent. + */ + private String fInitialLineDelimiter; + private static final String READ_ONLY_REGIONS_CATEGORY = "_READ_ONLY_REGIONS_CATEGORY_"; //$NON-NLS-1$ + /** + * Current rewrite session, or none if not presently rewriting. + */ + private DocumentRewriteSession fActiveRewriteSession; + /** + * Last modification stamp, automatically updated on change. + */ + private long fModificationStamp; + /** + * Keeps track of next modification stamp. + */ + private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; + /** + * debug variable only + * + * @param parser + */ + private long startStreamTime; + /** + * debug variable only + * + * @param parser + */ + private long startTime; + + public BasicStructuredDocument() { + super(); + fCurrentDocumentRegionCache = new CurrentDocumentRegionCache(); + setTextStore(new StructuredDocumentTextStore(50, 300)); + setLineTracker(new DefaultLineTracker()); + NULL_DOCUMENT_EVENT = new NullDocumentEvent(); + + internal_addPositionCategory(READ_ONLY_REGIONS_CATEGORY); + internal_addPositionUpdater(new DeleteEqualPositionUpdater(READ_ONLY_REGIONS_CATEGORY)); + + } + + /** + * This is the primary way to get a new structuredDocument. Its best to + * use the factory methods in ModelManger to create a new + * IStructuredDocument, since it will get and initialize the parser + * according to the desired content type. + */ + public BasicStructuredDocument(RegionParser parser) { + this(); + Assert.isNotNull(parser, "Program Error: IStructuredDocument can not be created with null parser"); //$NON-NLS-1$ + // go through setter in case there is side effects + internal_setParser(parser); + } + + private void _clearDocumentEvent() { + // no hard and fast requirement to null out ... just seems like + // a good idea, since we are done with it. + fDocumentEvent = null; + } + + private void _fireDocumentAboutToChange(Object[] listeners) { + // most DocumentAboutToBeChanged listeners do not anticipate + // DocumentEvent == null. So make sure documentEvent is not + // null. (this should never happen, yet it does sometimes) + if (fDocumentEvent == null) { + fDocumentEvent = new NullDocumentEvent(); + } + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + // Note: the docEvent is created in replaceText API + // fire + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + // safeguard from listeners that throw exceptions + try { + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + ((IDocumentListener) holdListeners[i]).documentAboutToBeChanged(fDocumentEvent); + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + private void notifyDocumentPartitionersAboutToChange(DocumentEvent documentEvent) { + if (fDocumentPartitioners != null) { + Iterator e = fDocumentPartitioners.values().iterator(); + while (e.hasNext()) { + IDocumentPartitioner p = (IDocumentPartitioner) e.next(); + // safeguard from listeners that throw exceptions + try { + p.documentAboutToBeChanged(documentEvent); + } + catch (Exception exception) { + Logger.logException(exception); + } + } + } + } + + private void _fireDocumentChanged(Object[] listeners, StructuredDocumentEvent event) { + + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + // NOTE: document event is created in replace Text API and setText + // API + // now fire + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + + // safeguard from listeners that throw exceptions + try { + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + // Notes: fDocumentEvent can be "suddenly" null, if one of + // the + // previous changes + // caused a "setText" to be called. The only known case of + // this + // is a model reset + // due to page directive changing. Eventually we should + // change + // archetecture to have + // event que and be able to "cancel" pending events, but + // for + // now, we'll just pass a + // NullDocumentEvent. By the way, it is important to send + // something, since clients might + // have indeterminant state due to "aboutToChange" being + // sent + // earlier. + if (fDocumentEvent == null) { + ((IDocumentListener) holdListeners[i]).documentChanged(NULL_DOCUMENT_EVENT); + } + else { + ((IDocumentListener) holdListeners[i]).documentChanged(fDocumentEvent); + } + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + private void notifyDocumentPartitionersDocumentChanged(DocumentEvent documentEvent) { + if (fDocumentPartitioners != null) { + Iterator e = fDocumentPartitioners.values().iterator(); + while (e.hasNext()) { + IDocumentPartitioner p = (IDocumentPartitioner) e.next(); + // safeguard from listeners that throw exceptions + try { + if (p instanceof IDocumentPartitionerExtension) { + // IRegion changedPartion = + ((IDocumentPartitionerExtension) p).documentChanged2(documentEvent); + } + else { + p.documentChanged(documentEvent); + } + } + catch (Exception exception) { + Logger.logException(exception); + } + } + } + } + + + private void _fireEvent(Object[] listeners, NoChangeEvent event) { + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + // safeguard from listeners that throw exceptions + try { + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + ((IStructuredDocumentListener) holdListeners[i]).noChange(event); + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + private void _fireEvent(Object[] listeners, RegionChangedEvent event) { + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + // safeguard from listeners that throw exceptions + try { + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + ((IStructuredDocumentListener) holdListeners[i]).regionChanged(event); + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + private void _fireEvent(Object[] listeners, RegionsReplacedEvent event) { + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + // safeguard from listeners that throw exceptions + try { + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + ((IStructuredDocumentListener) holdListeners[i]).regionsReplaced(event); + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + private void _fireEvent(Object[] listeners, StructuredDocumentRegionsReplacedEvent event) { + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + // safeguard from listeners that throw exceptions + try { + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + ((IStructuredDocumentListener) holdListeners[i]).nodesReplaced(event); + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + private void _fireStructuredDocumentAboutToChange(Object[] listeners) { + // we must assign listeners to local variable, since the add and + // remove + // listner + // methods can change the actual instance of the listener array from + // another thread + if (listeners != null) { + Object[] holdListeners = listeners; + // Note: the docEvent is created in replaceText API + // fire + for (int i = 0; i < holdListeners.length; i++) { + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + startTime = System.currentTimeMillis(); + } + // safeguard from listeners that throw exceptions + try { + // notice the AboutToBeChangedEvent is created from the + // DocumentEvent, since it is (nearly) + // the same information. ?What to do about + // originalRequester? + if (fDocumentEvent == null) { + fDocumentEvent = new NullDocumentEvent(); + } + AboutToBeChangedEvent aboutToBeChangedEvent = new AboutToBeChangedEvent(this, null, fDocumentEvent.getText(), fDocumentEvent.getOffset(), fDocumentEvent.getLength()); + // this is a safe cast, since addListners requires a + // IStructuredDocumentListener + ((IModelAboutToBeChangedListener) holdListeners[i]).modelAboutToBeChanged(aboutToBeChangedEvent); + } + catch (Exception exception) { + Logger.logException(exception); + } + if (Debug.perfTest || Debug.perfTestStructuredDocumentEventOnly) { + long stopTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t IStructuredDocument::fireStructuredDocumentEvent. Time was " + (stopTime - startTime) + " msecs to fire NewModelEvent to instance of " + holdListeners[i].getClass()); //$NON-NLS-2$//$NON-NLS-1$ + } + } + } + } + + protected void acquireLock() { + // do nothing here in super class + } + + /** + * addModelAboutToBeChangedListener method comment. + */ + public void addDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener) { + synchronized (listenerLock) { + + // make sure listener is not already in listening + // (and if it is, print a warning to aid debugging, if needed) + if (!Utilities.contains(fStructuredDocumentAboutToChangeListeners, listener)) { + int oldSize = 0; + if (fStructuredDocumentAboutToChangeListeners != null) { + // normally won't be null, but we need to be sure, for + // first + // time through + oldSize = fStructuredDocumentAboutToChangeListeners.length; + } + int newSize = oldSize + 1; + Object[] newListeners = new Object[newSize]; + if (fStructuredDocumentAboutToChangeListeners != null) { + System.arraycopy(fStructuredDocumentAboutToChangeListeners, 0, newListeners, 0, oldSize); + } + // add listener to last position + newListeners[newSize - 1] = listener; + // + // now switch new for old + fStructuredDocumentAboutToChangeListeners = newListeners; + // + } + } + } + + /** + * The StructuredDocumentListners and ModelChagnedListeners are very + * similar. They both receive identical events. The difference is the + * timing. The "pure" StructuredDocumentListners are notified after the + * structuredDocument has been changed, but before other, related models + * may have been changed such as the Structural Model. The Structural + * model is in fact itself a "pure" StructuredDocumentListner. The + * ModelChangedListeners can rest assured that all models and data have + * been updated from the change by the tiem they are notified. This is + * especially important for the text widget, for example, which may rely + * on both structuredDocument and structural model information. + */ + public void addDocumentChangedListener(IStructuredDocumentListener listener) { + synchronized (listenerLock) { + + if (Debug.debugStructuredDocument) { + System.out.println("IStructuredDocument::addModelChangedListener. Request to add an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$ + } + // make sure listener is not already in listening + // (and if it is, print a warning to aid debugging, if needed) + if (Utilities.contains(fStructuredDocumentChangedListeners, listener)) { + if (Debug.displayWarnings) { + System.out.println("IStructuredDocument::addModelChangedListener. listener " + listener + " was addeded more than once. "); //$NON-NLS-2$//$NON-NLS-1$ + } + } + else { + if (Debug.debugStructuredDocument) { + System.out.println("IStructuredDocument::addModelChangedListener. Adding an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$ + } + int oldSize = 0; + if (fStructuredDocumentChangedListeners != null) { + // normally won't be null, but we need to be sure, for + // first + // time through + oldSize = fStructuredDocumentChangedListeners.length; + } + int newSize = oldSize + 1; + Object[] newListeners = new Object[newSize]; + if (fStructuredDocumentChangedListeners != null) { + System.arraycopy(fStructuredDocumentChangedListeners, 0, newListeners, 0, oldSize); + } + // add listener to last position + newListeners[newSize - 1] = listener; + // + // now switch new for old + fStructuredDocumentChangedListeners = newListeners; + // + // when a listener is added, + // send the new model event to that one particular listener, + // so it + // can initialize itself with the current state of the model + // listener.newModel(new NewModelEvent(this, listener)); + } + } + } + + public void addDocumentChangingListener(IStructuredDocumentListener listener) { + synchronized (listenerLock) { + + if (Debug.debugStructuredDocument) { + System.out.println("IStructuredDocument::addStructuredDocumentListener. Request to add an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$ + } + // make sure listener is not already in listening + // (and if it is, print a warning to aid debugging, if needed) + if (Utilities.contains(fStructuredDocumentChangingListeners, listener)) { + if (Debug.displayWarnings) { + System.out.println("IStructuredDocument::addStructuredDocumentListener. listener " + listener + " was addeded more than once. "); //$NON-NLS-2$//$NON-NLS-1$ + } + } + else { + if (Debug.debugStructuredDocument) { + System.out.println("IStructuredDocument::addStructuredDocumentListener. Adding an instance of " + listener.getClass() + " as a listener on structuredDocument."); //$NON-NLS-2$//$NON-NLS-1$ + } + int oldSize = 0; + if (fStructuredDocumentChangingListeners != null) { + // normally won't be null, but we need to be sure, for + // first + // time through + oldSize = fStructuredDocumentChangingListeners.length; + } + int newSize = oldSize + 1; + Object[] newListeners = new Object[newSize]; + if (fStructuredDocumentChangingListeners != null) { + System.arraycopy(fStructuredDocumentChangingListeners, 0, newListeners, 0, oldSize); + } + // add listener to last position + newListeners[newSize - 1] = listener; + // + // now switch new for old + fStructuredDocumentChangingListeners = newListeners; + // + // when a listener is added, + // send the new model event to that one particular listener, + // so it + // can initialize itself with the current state of the model + // listener.newModel(new NewModelEvent(this, listener)); + } + } + } + + /** + * We manage our own document listners, instead of delegating to our + * parentDocument, so we can fire at very end (and not when the + * parentDocument changes). + * + */ + public void addDocumentListener(IDocumentListener listener) { + synchronized (listenerLock) { + + // make sure listener is not already in listening + // (and if it is, print a warning to aid debugging, if needed) + if (!Utilities.contains(fDocumentListeners, listener)) { + int oldSize = 0; + if (fDocumentListeners != null) { + // normally won't be null, but we need to be sure, for + // first + // time through + oldSize = fDocumentListeners.length; + } + int newSize = oldSize + 1; + IDocumentListener[] newListeners = null; + newListeners = new IDocumentListener[newSize]; + if (fDocumentListeners != null) { + System.arraycopy(fDocumentListeners, 0, newListeners, 0, oldSize); + } + // add listener to last position + newListeners[newSize - 1] = listener; + // now switch new for old + fDocumentListeners = newListeners; + } + } + } + + /* + * @see org.eclipse.jface.text.IDocument#addDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener) + * + * Registers the document partitioning listener with the document. After + * registration the IDocumentPartitioningListener is informed about each + * partition change cause by a document manipulation. If a document + * partitioning listener is also a document listener, the following + * notification sequence is guaranteed if a document manipulation changes + * the document partitioning: 1) + * listener.documentAboutToBeChanged(DocumentEvent); 2) + * listener.documentPartitioningChanged(); 3) + * listener.documentChanged(DocumentEvent); If the listener is already + * registered nothing happens. + * + * @see IDocumentPartitioningListener + */ + + public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) { + synchronized (listenerLock) { + + Assert.isNotNull(listener); + if (fDocumentPartitioningListeners == null) { + fDocumentPartitioningListeners = new ArrayList(1); + } + if (!fDocumentPartitioningListeners.contains(listener)) + fDocumentPartitioningListeners.add(listener); + } + } + + /** + * Adds the position to the document's default position category. The + * default category must be specified by the implementer. A position that + * has been added to a position category is updated at each change applied + * to the document. + * + * @exception BadLocationException + * If position is not a valid range in the document + */ + public void addPosition(Position position) throws BadLocationException { + getPositionManager().addPosition(position); + } + + /** + * @see IDocument#addPosition + * @exception BadLocationException + * If position is not a valid range in the document + * @exception BadPositionCategoryException + * If the category is not defined for the document + */ + public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException { + getPositionManager().addPosition(category, position); + } + + /** + * @see IDocument#addPositionCategory + */ + public void addPositionCategory(String category) { + internal_addPositionCategory(category); + } + + /** + * @see IDocument#addPositionUpdater + */ + public void addPositionUpdater(IPositionUpdater updater) { + internal_addPositionUpdater(updater); + } + + /** + * Adds the given document listener as one which is notified before those + * document listeners added with <code>addDocumentListener</code> are + * notified. If the given listener is also registered using + * <code>addDocumentListener</code> it will be notified twice. If the + * listener is already registered nothing happens. + * <p> + * + * This method is not for public use, it may only be called by + * implementers of <code>IDocumentAdapter</code> and only if those + * implementers need to implement <code>IDocumentListener</code>. + * + * @param documentAdapter + * the listener to be added as prenotified document listener + */ + public void addPrenotifiedDocumentListener(IDocumentListener documentAdapter) { + synchronized (listenerLock) { + + if (fPrenotifiedDocumentListeners != null) { + int previousSize = fPrenotifiedDocumentListeners.length; + IDocumentListener[] listeners = new IDocumentListener[previousSize + 1]; + System.arraycopy(fPrenotifiedDocumentListeners, 0, listeners, 0, previousSize); + listeners[previousSize] = documentAdapter; + fPrenotifiedDocumentListeners = listeners; + } + else { + fPrenotifiedDocumentListeners = new IDocumentListener[1]; + fPrenotifiedDocumentListeners[0] = documentAdapter; + } + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#charAt(int) + */ + public char charAt(int arg0) { + try { + return getChar(0); + } + catch (BadLocationException e) { + throw new IndexOutOfBoundsException(); + } + } + + /** + * This form of the API removes all read only positions, as should be done + * we 'setText' is called. Note: an alternative algorithm may simply + * remove the category (and it would get added back in later, if/when + * readonly regions added. + */ + private void clearReadOnly() { + Position[] positions = null; + try { + positions = getPositions(READ_ONLY_REGIONS_CATEGORY); + } + catch (BadPositionCategoryException e) { + Logger.logException("program error: should never occur", e); //$NON-NLS-1$ + } + for (int i = 0; i < positions.length; i++) { + Position position = positions[i]; + // note we don't fire the "about to change" or "changed" events, + // since presumably, text is all going away and being replaced + // anyway. + position.delete(); + } + } + + + public void clearReadOnly(int startOffset, int length) { + // TODO DW I still need to implement smarter algorithm that + // adust existing RO regions, if needed. For now, I'll just + // remove any that overlap. + try { + Position[] positions = getPositions(READ_ONLY_REGIONS_CATEGORY); + for (int i = 0; i < positions.length; i++) { + Position position = positions[i]; + if (position.overlapsWith(startOffset, length)) { + String effectedText = this.get(startOffset, length); + // fDocumentEvent = new DocumentEvent(this, startOffset, + // length, effectedText); + fireReadOnlyAboutToBeChanged(); + position.delete(); + NoChangeEvent noChangeEvent = new NoChangeEvent(this, null, effectedText, startOffset, length); + noChangeEvent.reason = NoChangeEvent.READ_ONLY_STATE_CHANGE; + fireReadOnlyStructuredDocumentEvent(noChangeEvent); + } + } + } + catch (BadPositionCategoryException e) { + // just means no readonly regions been defined yet + // so nothing to do. + } + } + + /** + * Computes the index at which a <code>Position</code> with the + * specified offset would be inserted into the given category. As the + * ordering inside a category only depends on the offset, the index must + * be choosen to be the first of all positions with the same offset. + * + * @param category + * the category in which would be added + * @param offset + * the position offset to be considered + * @return the index into the category + * @exception BadLocationException + * if offset is invalid in this document + * @exception BadPositionCategoryException + * if category is undefined in this document + */ + public int computeIndexInCategory(String category, int offset) throws org.eclipse.jface.text.BadPositionCategoryException, org.eclipse.jface.text.BadLocationException { + return getPositionManager().computeIndexInCategory(category, offset); + } + + /** + * Computes the number of lines in the given text. For a given implementer + * of this interface this method returns the same result as + * <code>set(text); getNumberOfLines()</code>. + * + * @param text + * the text whose number of lines should be computed + * @return the number of lines in the given text + */ + public int computeNumberOfLines(String text) { + return getTracker().computeNumberOfLines(text); + } + + /** + * Computes the partitioning of the given document range using the + * document's partitioner. + * + * @param offset + * the document offset at which the range starts + * @param length + * the length of the document range + * @return a specification of the range's partitioning + * @throws BadLocationException + * @throws BadPartitioningException + */ + public ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException { + ITypedRegion[] typedRegions = null; + try { + typedRegions = computePartitioning(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, offset, length, false); + } + catch (BadPartitioningException e) { + // impossible in this context + throw new Error(e); + } + if (typedRegions == null) { + typedRegions = new ITypedRegion[0]; + } + return typedRegions; + } + + + public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, boolean includeZeroLengthPartitions) throws BadLocationException, BadPartitioningException { + if ((0 > offset) || (0 > length) || (offset + length > getLength())) + throw new BadLocationException(); + + IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning); + + if (partitioner instanceof IDocumentPartitionerExtension2) + return ((IDocumentPartitionerExtension2) partitioner).computePartitioning(offset, length, includeZeroLengthPartitions); + else if (partitioner != null) + return partitioner.computePartitioning(offset, length); + else if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning)) + return new TypedRegion[]{new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE)}; + else + throw new BadPartitioningException(); + } + + /** + * @see IDocument#containsPosition + */ + public boolean containsPosition(String category, int offset, int length) { + return getPositionManager().containsPosition(category, offset, length); + } + + /** + * @see IDocument#containsPositionCategory + */ + public boolean containsPositionCategory(String category) { + return getPositionManager().containsPositionCategory(category); + } + + public boolean containsReadOnly(int startOffset, int length) { + boolean result = false; + try { + Position[] positions = getPositions(READ_ONLY_REGIONS_CATEGORY); + for (int i = 0; i < positions.length; i++) { + Position position = positions[i]; + if (position.overlapsWith(startOffset, length)) { + result = true; + break; + } + } + } + catch (BadPositionCategoryException e) { + // just means no readonly regions been defined yet + // so obviously false + result = false; + } + return result; + } + + private void executePostNotificationChanges() { + if (fStoppedCount > 0) + return; + while (fPostNotificationChanges != null) { + List changes = fPostNotificationChanges; + fPostNotificationChanges = null; + Iterator e = changes.iterator(); + while (e.hasNext()) { + RegisteredReplace replace = (RegisteredReplace) e.next(); + replace.fReplace.perform(this, replace.fOwner); + } + } + } + + private void fireDocumentAboutToChanged() { + // most DocumentAboutToBeChanged listeners do not anticipate + // DocumentEvent == null. So make sure documentEvent is not + // null. (this should never happen, yet it does sometimes) + if (fDocumentEvent == null) { + fDocumentEvent = new NullDocumentEvent(); + } + + _fireStructuredDocumentAboutToChange(fStructuredDocumentAboutToChangeListeners); + // Note: the docEvent is created in replaceText API! (or set Text) + _fireDocumentAboutToChange(fPrenotifiedDocumentListeners); + notifyDocumentPartitionersAboutToChange(fDocumentEvent); + _fireDocumentAboutToChange(fDocumentListeners); + } + + /** + * Fires the document partitioning changed notification to all registered + * document partitioning listeners. Uses a robust iterator. + * + * @param event + * the document partitioning changed event + * + * @see IDocumentPartitioningListenerExtension2 + */ + protected void fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent event) { + if (fDocumentPartitioningListeners == null || fDocumentPartitioningListeners.size() == 0) + return; + + List list = new ArrayList(fDocumentPartitioningListeners); + Iterator e = list.iterator(); + while (e.hasNext()) { + IDocumentPartitioningListener l = (IDocumentPartitioningListener) e.next(); + if (l instanceof IDocumentPartitioningListenerExtension2) { + IDocumentPartitioningListenerExtension2 extension2 = (IDocumentPartitioningListenerExtension2) l; + extension2.documentPartitioningChanged(event); + } + else if (l instanceof IDocumentPartitioningListenerExtension) { + IDocumentPartitioningListenerExtension extension = (IDocumentPartitioningListenerExtension) l; + extension.documentPartitioningChanged(this, event.getCoverage()); + } + else { + l.documentPartitioningChanged(this); + } + } + + } + + private void fireReadOnlyAboutToBeChanged() { + _fireStructuredDocumentAboutToChange(fStructuredDocumentAboutToChangeListeners); + // Note: the docEvent is created in replaceText API! (or set Text) + // _fireDocumentAboutToChange(fPrenotifiedDocumentListeners); + // _fireDocumentAboutToChange(fDocumentListeners); + } + + private void fireReadOnlyStructuredDocumentEvent(NoChangeEvent event) { + _fireEvent(fStructuredDocumentChangingListeners, event); + _fireEvent(fStructuredDocumentChangedListeners, event); + // _fireDocumentChanged(fPrenotifiedDocumentListeners, event); + // _fireDocumentChanged(fDocumentListeners, event); + // _clearDocumentEvent(); + } + + private void fireStructuredDocumentEvent(NoChangeEvent event) { + _fireEvent(fStructuredDocumentChangingListeners, event); + _fireEvent(fStructuredDocumentChangedListeners, event); + _fireDocumentChanged(fPrenotifiedDocumentListeners, event); + notifyDocumentPartitionersDocumentChanged(event); + _fireDocumentChanged(fDocumentListeners, event); + _clearDocumentEvent(); + } + + private void fireStructuredDocumentEvent(RegionChangedEvent event) { + _fireEvent(fStructuredDocumentChangingListeners, event); + _fireEvent(fStructuredDocumentChangedListeners, event); + _fireDocumentChanged(fPrenotifiedDocumentListeners, event); + notifyDocumentPartitionersDocumentChanged(event); + _fireDocumentChanged(fDocumentListeners, event); + _clearDocumentEvent(); + } + + private void fireStructuredDocumentEvent(RegionsReplacedEvent event) { + _fireEvent(fStructuredDocumentChangingListeners, event); + _fireEvent(fStructuredDocumentChangedListeners, event); + _fireDocumentChanged(fPrenotifiedDocumentListeners, event); + notifyDocumentPartitionersDocumentChanged(event); + _fireDocumentChanged(fDocumentListeners, event); + _clearDocumentEvent(); + } + + private void fireStructuredDocumentEvent(StructuredDocumentRegionsReplacedEvent event) { + _fireEvent(fStructuredDocumentChangingListeners, event); + _fireEvent(fStructuredDocumentChangedListeners, event); + _fireDocumentChanged(fPrenotifiedDocumentListeners, event); + notifyDocumentPartitionersDocumentChanged(event); + _fireDocumentChanged(fDocumentListeners, event); + _clearDocumentEvent(); + } + + /** + * Returns the document's complete text. + */ + public String get() { + return getStore().get(0, getLength()); + } + + /** + * Returns length characters from the document's text starting from the + * specified position. + * + * @throws BadLocationException + * + * @exception BadLocationException + * If the range is not valid in the document + */ + public String get(int offset, int length) { + String result = null; + int myLength = getLength(); + if (0 > offset) + offset = 0; + if (0 > length) + length = 0; + if (offset + length > myLength) { + // first try adjusting length to fit + int lessLength = myLength - offset; + if ((lessLength >= 0) && (offset + lessLength == myLength)) { + length = lessLength; + } + else { + // second, try offset + int moreOffset = myLength - length; + if ((moreOffset >= 0) && (moreOffset + length == myLength)) { + offset = moreOffset; + } + else { + // can happen if myLength is 0. + // no adjustment possible. + result = new String(); + } + } + + } + if (result == null) { + result = getStore().get(offset, length); + } + return result; + } + + public Object getAdapter(Class adapter) { + return Platform.getAdapterManager().getAdapter(this, adapter); + } + + IStructuredDocumentRegion getCachedDocumentRegion() { + IStructuredDocumentRegion result = null; + if (USE_LOCAL_THREAD) { + result = fCurrentDocumentRegionCache.get(); + } + else { + result = cachedDocumentRegion; + } + return result; + } + + /** + * @see IDocument#getChar + * @exception BadLocationException + * If position is not a valid range in the document + */ + public char getChar(int pos) throws BadLocationException { + char result = 0x00; + try { + result = getStore().get(pos); + } + catch (IndexOutOfBoundsException e) { + throw new BadLocationException(e.getLocalizedMessage()); + } + return result; + } + + /** + * Returns the type of the document partition containing the given + * character position. + */ + public String getContentType(int offset) throws BadLocationException { + return getDocumentPartitioner().getContentType(offset); + } + + + public String getContentType(String partitioning, int offset, boolean preferOpenPartitions) throws BadLocationException, BadPartitioningException { + if ((0 > offset) || (offset > getLength())) + throw new BadLocationException(); + + IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning); + + if (partitioner instanceof IDocumentPartitionerExtension2) + return ((IDocumentPartitionerExtension2) partitioner).getContentType(offset, preferOpenPartitions); + else if (partitioner != null) + return partitioner.getContentType(offset); + else if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning)) + return DEFAULT_CONTENT_TYPE; + else + throw new BadPartitioningException(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#getDefaultLineDelimiter() + */ + public String getDefaultLineDelimiter() { + + String lineDelimiter= null; + + try { + lineDelimiter= getLineDelimiter(0); + } catch (BadLocationException x) { + } + + if (lineDelimiter != null) + return lineDelimiter; + + if (fInitialLineDelimiter != null) + return fInitialLineDelimiter; + + String sysLineDelimiter= System.getProperty("line.separator"); //$NON-NLS-1$ + String[] delimiters= getLegalLineDelimiters(); + Assert.isTrue(delimiters.length > 0); + for (int i= 0; i < delimiters.length; i++) { + if (delimiters[i].equals(sysLineDelimiter)) { + lineDelimiter= sysLineDelimiter; + break; + } + } + + if (lineDelimiter == null) + lineDelimiter= delimiters[0]; + + return lineDelimiter; + + } + + /** + * Returns the document's partitioner. + * + * @see IDocumentPartitioner + */ + public IDocumentPartitioner getDocumentPartitioner() { + return getDocumentPartitioner(IDocumentExtension3.DEFAULT_PARTITIONING); + } + + + public IDocumentPartitioner getDocumentPartitioner(String partitioning) { + + IDocumentPartitioner documentPartitioner = null; + if (fDocumentPartitioners != null) { + documentPartitioner = (IDocumentPartitioner) fDocumentPartitioners.get(partitioning); + } + return documentPartitioner; + } + + public EncodingMemento getEncodingMemento() { + return encodingMemento; + } + + public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + // should we update cachedNode? + // We should to keep consistent philosophy of remembering last + // requested position, + // for efficiency. + setCachedDocumentRegion(firstDocumentRegion); + return firstDocumentRegion; + } + + public IStructuredDocumentRegion getLastStructuredDocumentRegion() { + // should we update cachedNode? + // We should to keep consistent philosophy of remembering last + // requested position, + // for efficiency. + setCachedDocumentRegion(lastDocumentRegion); + return lastDocumentRegion; + } + + /* + * -------------------------- partitions + * ---------------------------------- + */ + public String[] getLegalContentTypes() { + String[] result = null; + try { + result = getLegalContentTypes(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING); + } + catch (BadPartitioningException e) { + // impossible in this context + throw new Error(e); + } + return result; + } + + public String[] getLegalContentTypes(String partitioning) throws BadPartitioningException { + IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning); + if (partitioner != null) + return partitioner.getLegalContentTypes(); + if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning)) + return new String[]{DEFAULT_CONTENT_TYPE}; + throw new BadPartitioningException(); + } + + /* + * ------------------ line delimiter conversion + * --------------------------- + */ + public String[] getLegalLineDelimiters() { + return getTracker().getLegalLineDelimiters(); + } + + /** + * @see IDocument#getLength + */ + public int getLength() { + return getStore().getLength(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument#getLineDelimiter() + */ + public String getLineDelimiter() { + return getDefaultLineDelimiter(); + } + + /** + * Returns the line delimiter of that line + * + * @exception BadLocationException + * If the line number is invalid in the document + */ + public String getLineDelimiter(int line) throws org.eclipse.jface.text.BadLocationException { + return getTracker().getLineDelimiter(line); + } + + /** + * Returns a description of the specified line. The line is described by + * its offset and its length excluding the line's delimiter. + * + * @param line + * the line of interest + * @return a line description + * @exception BadLocationException + * if the line number is invalid in this document + */ + public org.eclipse.jface.text.IRegion getLineInformation(int line) throws org.eclipse.jface.text.BadLocationException { + return getTracker().getLineInformation(line); + } + + /** + * Returns a description of the line at the given offset. The description + * contains the offset and the length of the line excluding the line's + * delimiter. + * + * @param offset + * the offset whose line should be described + * @return a region describing the line + * @exception BadLocationException + * if offset is invalid in this document + */ + public org.eclipse.jface.text.IRegion getLineInformationOfOffset(int offset) throws org.eclipse.jface.text.BadLocationException { + return getTracker().getLineInformationOfOffset(offset); + } + + /* + * ---------------------- line information + * -------------------------------- + */ + public int getLineLength(int line) throws org.eclipse.jface.text.BadLocationException { + return getTracker().getLineLength(line); + } + + /** + * Determines the offset of the first character of the given line. + * + * @param line + * the line of interest + * @return the document offset + * @exception BadLocationException + * if the line number is invalid in this document + */ + public int getLineOffset(int line) throws org.eclipse.jface.text.BadLocationException { + return getTracker().getLineOffset(line); + } + + public int getLineOfOffset(int offset) { + int result = -1; + try { + result = getTracker().getLineNumberOfOffset(offset); + } + catch (BadLocationException e) { + if (Logger.DEBUG_DOCUMENT) + Logger.log(Logger.INFO, "Dev. Program Info Only: IStructuredDocument::getLineOfOffset: offset out of range, zero assumed. offset = " + offset, e); //$NON-NLS-1$ //$NON-NLS-2$ + result = 0; + } + return result; + } + + /** + * Returns the number of lines in this document + * + * @return the number of lines in this document + */ + public int getNumberOfLines() { + return getTracker().getNumberOfLines(); + } + + /** + * Returns the number of lines which are occupied by a given text range. + * + * @param offset + * the offset of the specified text range + * @param length + * the length of the specified text range + * @return the number of lines occupied by the specified range + * @exception BadLocationException + * if specified range is invalid in this tracker + */ + public int getNumberOfLines(int offset, int length) throws org.eclipse.jface.text.BadLocationException { + return getTracker().getNumberOfLines(offset, length); + } + + /** + * This is public, temporarily, for use by tag lib classes. + */ + public RegionParser getParser() { + if (fParser == null) { + throw new IllegalStateException("IStructuredDocument::getParser. Parser needs to be set before use"); //$NON-NLS-1$ + } + return fParser; + } + + /** + * Returns the document partition in which the position is located. The + * partition is specified as typed region. + */ + public ITypedRegion getPartition(int offset) throws BadLocationException { + ITypedRegion partition = null; + try { + partition = getPartition(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, offset, false); + } + catch (BadPartitioningException e) { + throw new Error(e); + } + if (partition == null) { + throw new Error(); + } + return partition; + } + + + public ITypedRegion getPartition(String partitioning, int offset, boolean preferOpenPartitions) throws BadLocationException, BadPartitioningException { + if ((0 > offset) || (offset > getLength())) + throw new BadLocationException(); + ITypedRegion result = null; + + IDocumentPartitioner partitioner = getDocumentPartitioner(partitioning); + + if (partitioner instanceof IDocumentPartitionerExtension2) { + result = ((IDocumentPartitionerExtension2) partitioner).getPartition(offset, preferOpenPartitions); + } + else if (partitioner != null) { + result = partitioner.getPartition(offset); + } + else if (IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING.equals(partitioning)) { + result = new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE); + } + else + throw new BadPartitioningException(); + return result; + } + + + public String[] getPartitionings() { + if (fDocumentPartitioners == null) + return new String[0]; + String[] partitionings = new String[fDocumentPartitioners.size()]; + fDocumentPartitioners.keySet().toArray(partitionings); + return partitionings; + } + + /** + * Returns all position categories added to this document. + */ + public String[] getPositionCategories() { + return getPositionManager().getPositionCategories(); + } + + /** + * @return Returns the positionManager. + */ + private GenericPositionManager getPositionManager() { + if (fPositionManager == null) { + fPositionManager = new GenericPositionManager(this); + } + return fPositionManager; + } + + /** + * Returns all Positions of the given position category. + * + * @exception BadPositionCategoryException + * If category is not defined for the document + */ + public Position[] getPositions(String category) throws org.eclipse.jface.text.BadPositionCategoryException { + return getPositionManager().getPositions(category); + } + + /** + * @see IDocument#getPositionUpdaters + */ + public IPositionUpdater[] getPositionUpdaters() { + return getPositionManager().getPositionUpdaters(); + } + + /** + * This method can return null, which is the case if the offset is just + * before or just after the existing text. Compare with + * getNodeAtCharacterOffset. + */ + public IStructuredDocumentRegion getRegionAtCharacterOffset(int offset) { + IStructuredDocumentRegion result = null; + + // FIXME: need to synch on 'cachedRegion' (but since that's a + // constantly changing object, we + // can't, so need to add a "region_lock" object, and use it here, and + // in re-parser. + // Oh, and need to make sure, after synch, that the region is not + // deleted, and if so, I guess go back + // to the beginning! + + // cached node can be null when document is empty + IStructuredDocumentRegion potentialCachedRegion = getCachedDocumentRegion(); + if (potentialCachedRegion != null) { + + // + + // if we already have the right node, return that. + if (potentialCachedRegion.containsOffset(offset)) { + result = potentialCachedRegion; + } + else { + // first, find out what direction to go, relative to + // cachedNode. + // negative means "towards the front" of the file, + // postitive + // means + // towards the end. + int direction = offset - potentialCachedRegion.getStart(); + if (direction < 0) { + // search towards beginning + while (!potentialCachedRegion.containsOffset(offset)) { + IStructuredDocumentRegion tempNode = potentialCachedRegion.getPrevious(); + if (tempNode == null) { + break; + } + else { + potentialCachedRegion = tempNode; + } + } + } + else { + // search towards end + // There is a legitamat condition where the + // offset will not be contained in any node, + // which is if the offset is just past the last + // character of text. + // And, we must gaurd against setting cachedNode to + // null! + while (!potentialCachedRegion.containsOffset(offset)) { + IStructuredDocumentRegion tempNode = potentialCachedRegion.getNext(); + if (tempNode == null) + break; + else + potentialCachedRegion = tempNode; + } + } + } + result = potentialCachedRegion; + } + // just to be doubly sure we never assign null to an already valid + // cachedRegion. + // I believe any time 'result' is null at this point, that just means + // we have an + // empty document, and the cachedRegion is already null, but we check + // and print + // warning, just so during development we be sure we never accidently + // break this assumption. + if (result != null) + setCachedDocumentRegion(result); + else if (getCachedDocumentRegion() != null) { + throw new IllegalStateException("Program Error: no region could be found to cache, but cache was non null. Indicates corrupted model or region list"); //$NON-NLS-1$ + } + + return result; + } + + public IStructuredDocumentRegionList getRegionList() { + CoreNodeList result = null; + if (getCachedDocumentRegion() == null) + result = new CoreNodeList(null); + else + result = new CoreNodeList(getFirstStructuredDocumentRegion()); + + return result; + } + + + public IStructuredDocumentRegion[] getStructuredDocumentRegions() { + return getStructuredDocumentRegions(0, getLength()); + } + + /** + * <p> + * In the case of 0 length, the <code>IStructuredDocumentRegion</code> + * at the character offset is returened. In other words, the region to the + * right of the caret is returned. except for at the end of the document, + * then the last region is returned. + * </p> + * <p> + * Otherwise all the regions "inbetween" the indicated range are returned, + * including the regions which overlap the region. + * </p> + * + * <br> + * eg. + * <p> + * <br> + * eg. + * + * <pre> + * <html>[<head></head>]</html> returns <head>,</head> + * </pre> + * <pre> + * <ht[ml><head></he]ad></html> returns <html>,<head>,</head> + * </pre> + * + * <pre> + * <html>[<head></head>]</html> returns <head>,</head> + * </pre> + * <pre> + * <ht[ml><head></he]ad></html> returns <html>,<head>,</head> + * </pre> + * + * </p> + */ + public IStructuredDocumentRegion[] getStructuredDocumentRegions(int start, int length) { + + if (length < 0) + throw new IllegalArgumentException("can't have negative length"); //$NON-NLS-1$ + + // this will make the right edge of the range point into the selection + // eg. <html>[<head></head>]</html> + // will return <head>,</head> instead of <head>,</head>,</html> + if (length > 0) + length--; + + List results = new ArrayList(); + + // start thread safe block + try { + acquireLock(); + + IStructuredDocumentRegion currentRegion = getRegionAtCharacterOffset(start); + IStructuredDocumentRegion endRegion = getRegionAtCharacterOffset(start + length); + while (currentRegion != endRegion && currentRegion != null) { + results.add(currentRegion); + currentRegion = currentRegion.getNext(); + } + // need to add that last end region + // can be null in the case of an empty document + if (endRegion != null) + results.add(endRegion); + } + finally { + releaseLock(); + } + // end thread safe block + + return (IStructuredDocumentRegion[]) results.toArray(new IStructuredDocumentRegion[results.size()]); + } + + /** + * was made public for easier testing. Normally should never be used by + * client codes. + */ + public IStructuredTextReParser getReParser() { + if (fReParser == null) { + fReParser = new StructuredDocumentReParser(); + fReParser.setStructuredDocument(this); + } + return fReParser; + } + + private ITextStore getStore() { + return fStore; + } + + public String getText() { + String result = get(); + return result; + } + + /** + * Returns the document's line tracker. Assumes that the document has been + * initialized with a line tracker. + * + * @return the document's line tracker + */ + private ILineTracker getTracker() { + return fTracker; + } + + public IStructuredTextUndoManager getUndoManager() { + if (fUndoManager == null) { + fUndoManager = new StructuredTextUndoManager(); + } + return fUndoManager; + } + + void initializeFirstAndLastDocumentRegion() { + // cached Node must also be first, at the initial point. Only + // valid + // to call this method right after the first parse. + // + // when starting afresh, our cachedNode should be our firstNode, + // so be sure to initialize the firstNode + firstDocumentRegion = getCachedDocumentRegion(); + // be sure to use 'getNext' for this initial finding of the last + // node, + // since the implementation of node.getLastNode may simply call + // structuredDocument.getLastStructuredDocumentRegion! + IStructuredDocumentRegion aNode = firstDocumentRegion; + if (aNode == null) { + // defect 254607: to handle empty documents right, if + // firstnode is + // null, make sure last node is null too + lastDocumentRegion = null; + } + else { + while (aNode != null) { + lastDocumentRegion = aNode; + aNode = aNode.getNext(); + } + } + } + + /** + * @see IDocument#insertPositionUpdater + */ + public void insertPositionUpdater(IPositionUpdater updater, int index) { + getPositionManager().insertPositionUpdater(updater, index); + } + + private void internal_addPositionCategory(String category) { + getPositionManager().addPositionCategory(category); + } + + private void internal_addPositionUpdater(IPositionUpdater updater) { + getPositionManager().addPositionUpdater(updater); + } + + private void internal_setParser(RegionParser newParser) { + fParser = newParser; + } + + String internalGet(int offset, int length) { + String result = null; + // int myLength = getLength(); + // if ((0 > offset) || (0 > length) || (offset + length > myLength)) + // throw new BadLocationException(); + result = getStore().get(offset, length); + return result; + } + + /** + * @param requester + * @param start + * @param replacementLength + * @param changes + * @param modificationStamp + * @param ignoreReadOnlySettings + * @return + */ + private StructuredDocumentEvent internalReplaceText(Object requester, int start, int replacementLength, String changes, long modificationStamp, boolean ignoreReadOnlySettings) { + StructuredDocumentEvent result = null; + + stopPostNotificationProcessing(); + if (changes == null) + changes = ""; //$NON-NLS-1$ + // + if (Debug.debugStructuredDocument) + System.out.println(getClass().getName() + "::replaceText(" + start + "," + replacementLength + "," + changes + ")"); //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + if (Debug.perfTestStructuredDocumentOnly || Debug.perfTest || Debug.perfTestRawStructuredDocumentOnly) { + startStreamTime = System.currentTimeMillis(); + } + try { + // Note: event must be computed before 'fire' method called + fDocumentEvent = new DocumentEvent(this, start, replacementLength, changes); + fireDocumentAboutToChanged(); + + try { + acquireLock(); + + if (!ignoreReadOnlySettings && (containsReadOnly(start, replacementLength))) { + NoChangeEvent noChangeEvent = new NoChangeEvent(this, requester, changes, start, replacementLength); + noChangeEvent.reason = NoChangeEvent.READ_ONLY_STATE_CHANGE; + result = noChangeEvent; + } + else { + result = updateModel(requester, start, replacementLength, changes); + } + } + finally { + releaseLock(); + } + + + if (Debug.perfTestRawStructuredDocumentOnly || Debug.perfTest) { + long stopStreamTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t Time for IStructuredDocument raw replaceText: " + (stopStreamTime - startStreamTime)); //$NON-NLS-1$ + } + if (Debug.debugStructuredDocument) { + System.out.println("event type returned by replaceTextWithNoDebuggingThread: " + result); //$NON-NLS-1$ + } + } + finally { + // FUTURE_TO_DO: implement callback mechanism? to avoid instanceof + // and casting + // fireStructuredDocumentEvent must be called in order to end + // documentAboutToBeChanged state + + + // increment modification stamp if modifications were made + if (result != null && !(result instanceof NoChangeEvent)) { + fModificationStamp= modificationStamp; + fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp); + fDocumentEvent.fModificationStamp = fModificationStamp; + } + + if (result == null) { + // result should not be null, but if an exception was thrown, + // it will be + // so send a noChangeEvent and log the problem + NoChangeEvent noChangeEvent = new NoChangeEvent(this, requester, changes, start, replacementLength); + noChangeEvent.reason = NoChangeEvent.NO_EVENT; + fireStructuredDocumentEvent(noChangeEvent); + Logger.log(Logger.ERROR, "Program Error: invalid structured document event"); //$NON-NLS-1$ + } + else { + if (result instanceof RegionChangedEvent) { + fireStructuredDocumentEvent((RegionChangedEvent) result); + } + else { + if (result instanceof RegionsReplacedEvent) { + fireStructuredDocumentEvent((RegionsReplacedEvent) result); + } + else { + if (result instanceof StructuredDocumentRegionsReplacedEvent) { + // probably more efficient to mark old regions as + // 'deleted' at the time + // that are determined to be deleted, but I'll do + // here + // in then central spot + // for programming ease. + updateDeletedFields((StructuredDocumentRegionsReplacedEvent) result); + fireStructuredDocumentEvent((StructuredDocumentRegionsReplacedEvent) result); + } + else { + if (result instanceof NoChangeEvent) { + fireStructuredDocumentEvent((NoChangeEvent) result); + } + else { + // if here, this means a new event was created + // and not handled here + // just send a no event until this issue is + // resolved. + NoChangeEvent noChangeEvent = new NoChangeEvent(this, requester, changes, start, replacementLength); + noChangeEvent.reason = NoChangeEvent.NO_EVENT; + fireStructuredDocumentEvent(noChangeEvent); + Logger.log(Logger.INFO, "Program Error: unexpected structured document event: " + result); //$NON-NLS-1$ + } + } + } + } + } + + if (Debug.perfTest || Debug.perfTestStructuredDocumentOnly) { + long stopStreamTime = System.currentTimeMillis(); + System.out.println("\n\t\t\t\t Total Time for IStructuredDocument event signaling/processing in replaceText: " + (stopStreamTime - startStreamTime)); //$NON-NLS-1$ + } + resumePostNotificationProcessing(); + } + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#length() + */ + public int length() { + + return getLength(); + } + + public void makeReadOnly(int startOffset, int length) { + makeReadOnly(startOffset, length, false, false); + } + + public void makeReadOnly(int startOffset, int length, boolean canInsertBefore, boolean canInsertAfter) { + // doesn't make sense to have a readonly region of 0 length, + // so we'll ignore those requests + if (length <= 0) + return; + String affectedText = this.get(startOffset, length); + // a document event for "read only" change ... must + // be followed by "no change" structuredDocument event + // fDocumentEvent = new DocumentEvent(this, startOffset, length, + // affectedText); + fireReadOnlyAboutToBeChanged(); + // if (containsReadOnly(startOffset, length)) { + // adjustReadOnlyRegions(startOffset, length); + // } else { + // we can blindly add category, since no harm done if already + // exists. + addPositionCategory(READ_ONLY_REGIONS_CATEGORY); + Position newPosition = new ReadOnlyPosition(startOffset, length, canInsertBefore); + try { + addPosition(READ_ONLY_REGIONS_CATEGORY, newPosition); + // FIXME: need to change API to pass in requester, so this event + // can be + // created correctly, instead of using null. + NoChangeEvent noChangeEvent = new NoChangeEvent(this, null, affectedText, startOffset, length); + noChangeEvent.reason = NoChangeEvent.READ_ONLY_STATE_CHANGE; + fireReadOnlyStructuredDocumentEvent(noChangeEvent); + } + catch (BadLocationException e) { + // for now, log and ignore. Perhaps later we + // could adjust to handle some cases? + Logger.logException(("could not create readonly region at " + startOffset + " to " + length), e); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (BadPositionCategoryException e) { + // should never occur, since we add category + Logger.logException(e); + } + } + + public IStructuredDocument newInstance() { + IStructuredDocument newInstance = StructuredDocumentFactory.getNewStructuredDocumentInstance(getParser().newInstance()); + ((BasicStructuredDocument) newInstance).setReParser(getReParser().newInstance()); + if (getDocumentPartitioner() instanceof StructuredTextPartitioner) { + newInstance.setDocumentPartitioner(((StructuredTextPartitioner) getDocumentPartitioner()).newInstance()); + newInstance.getDocumentPartitioner().connect(newInstance); + } + newInstance.setLineDelimiter(getLineDelimiter()); + if (getEncodingMemento() != null) { + newInstance.setEncodingMemento((EncodingMemento) getEncodingMemento().clone()); + } + return newInstance; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.text.IRegionComparible#regionMatches(int, + * int, java.lang.String) + */ + public boolean regionMatches(int offset, int length, String stringToCompare) { + boolean result = false; + ITextStore store = getStore(); + if (store instanceof IRegionComparible) { + result = ((IRegionComparible) store).regionMatches(offset, length, stringToCompare); + } + else { + result = get(offset, length).equals(stringToCompare); + } + return result; + } + + public boolean regionMatchesIgnoreCase(int offset, int length, String stringToCompare) { + boolean result = false; + ITextStore store = getStore(); + if (store instanceof IRegionComparible) { + result = ((IRegionComparible) store).regionMatchesIgnoreCase(offset, length, stringToCompare); + } + else { + result = get(offset, length).equalsIgnoreCase(stringToCompare); + } + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener, + * org.eclipse.jface.text.IDocumentExtension.IReplace) + */ + public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) { + if (fAcceptPostNotificationReplaces) { + if (fPostNotificationChanges == null) + fPostNotificationChanges = new ArrayList(1); + fPostNotificationChanges.add(new RegisteredReplace(owner, replace)); + } + } + + protected void releaseLock() { + // do nothing here in super class + } + + public void removeDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener) { + synchronized (listenerLock) { + + if ((fStructuredDocumentAboutToChangeListeners != null) && (listener != null)) { + // if its not in the listeners, we'll ignore the request + if (Utilities.contains(fStructuredDocumentAboutToChangeListeners, listener)) { + int oldSize = fStructuredDocumentAboutToChangeListeners.length; + int newSize = oldSize - 1; + Object[] newListeners = new Object[newSize]; + int index = 0; + for (int i = 0; i < oldSize; i++) { + if (fStructuredDocumentAboutToChangeListeners[i] == listener) { // ignore + } + else { + // copy old to new if its not the one we are + // removing + newListeners[index++] = fStructuredDocumentAboutToChangeListeners[i]; + } + } + // now that we have a new array, let's switch it for the + // old + // one + fStructuredDocumentAboutToChangeListeners = newListeners; + } + } + } + } + + /** + * removeModelChangedListener method comment. + */ + public void removeDocumentChangedListener(IStructuredDocumentListener listener) { + synchronized (listenerLock) { + + if ((fStructuredDocumentChangedListeners != null) && (listener != null)) { + // if its not in the listeners, we'll ignore the request + if (Utilities.contains(fStructuredDocumentChangedListeners, listener)) { + int oldSize = fStructuredDocumentChangedListeners.length; + int newSize = oldSize - 1; + Object[] newListeners = new Object[newSize]; + int index = 0; + for (int i = 0; i < oldSize; i++) { + if (fStructuredDocumentChangedListeners[i] == listener) { // ignore + } + else { + // copy old to new if its not the one we are + // removing + newListeners[index++] = fStructuredDocumentChangedListeners[i]; + } + } + // now that we have a new array, let's switch it for the + // old + // one + fStructuredDocumentChangedListeners = newListeners; + } + } + } + } + + public void removeDocumentChangingListener(IStructuredDocumentListener listener) { + synchronized (listenerLock) { + + if ((fStructuredDocumentChangingListeners != null) && (listener != null)) { + // if its not in the listeners, we'll ignore the request + if (Utilities.contains(fStructuredDocumentChangingListeners, listener)) { + int oldSize = fStructuredDocumentChangingListeners.length; + int newSize = oldSize - 1; + Object[] newListeners = new Object[newSize]; + int index = 0; + for (int i = 0; i < oldSize; i++) { + if (fStructuredDocumentChangingListeners[i] == listener) { // ignore + } + else { + // copy old to new if its not the one we are + // removing + newListeners[index++] = fStructuredDocumentChangingListeners[i]; + } + } + // now that we have a new array, let's switch it for the + // old + // one + fStructuredDocumentChangingListeners = newListeners; + } + } + } + } + + public void removeDocumentListener(IDocumentListener listener) { + synchronized (listenerLock) { + + if ((fDocumentListeners != null) && (listener != null)) { + // if its not in the listeners, we'll ignore the request + if (Utilities.contains(fDocumentListeners, listener)) { + int oldSize = fDocumentListeners.length; + int newSize = oldSize - 1; + IDocumentListener[] newListeners = new IDocumentListener[newSize]; + int index = 0; + for (int i = 0; i < oldSize; i++) { + if (fDocumentListeners[i] == listener) { // ignore + } + else { + // copy old to new if its not the one we are + // removing + newListeners[index++] = fDocumentListeners[i]; + } + } + // now that we have a new array, let's switch it for the + // old + // one + fDocumentListeners = newListeners; + } + } + } + } + + /* + * @see org.eclipse.jface.text.IDocument#removeDocumentPartitioningListener(org.eclipse.jface.text.IDocumentPartitioningListener) + */ + public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) { + synchronized (listenerLock) { + + Assert.isNotNull(listener); + if (fDocumentPartitioningListeners != null) + fDocumentPartitioningListeners.remove(listener); + } + } + + /** + * Removes the given <code>Position</code> from the document's default + * position category. The default position category is to be defined by + * the implementers. If the position is not part of the document's default + * category nothing happens. + */ + public void removePosition(Position position) { + getPositionManager().removePosition(position); + } + + /** + * @see IDocument#removePosition + * @exception BadPositionCategoryException + * If the category is not defined for the document + */ + public void removePosition(String category, Position position) throws BadPositionCategoryException { + getPositionManager().removePosition(category, position); + } + + /** + * @see IDocument#removePositionCategory + * @exception BadPositionCategoryException + * If the category is not defined for the document + */ + public void removePositionCategory(String category) throws BadPositionCategoryException { + getPositionManager().removePositionCategory(category); + } + + /** + * @see IDocument#removePositionUpdater + */ + public void removePositionUpdater(IPositionUpdater updater) { + getPositionManager().removePositionUpdater(updater); + } + + /** + * Removes the given document listener from teh document's list of + * prenotified document listeners. If the listener is not registered with + * the document nothing happens. + * <p> + * + * This method is not for public use, it may only be called by + * implementers of <code>IDocumentAdapter</code> and only if those + * implementers need to implement <code>IDocumentListener</code>. + * + * @param documentAdapter + * the listener to be removed + * + * @see #addPrenotifiedDocumentListener(IDocumentListener) + */ + public void removePrenotifiedDocumentListener(org.eclipse.jface.text.IDocumentListener documentAdapter) { + synchronized (listenerLock) { + + if (Utilities.contains(fPrenotifiedDocumentListeners, documentAdapter)) { + int previousSize = fPrenotifiedDocumentListeners.length; + if (previousSize > 1) { + IDocumentListener[] listeners = new IDocumentListener[previousSize - 1]; + int previousIndex = 0; + int newIndex = 0; + while (previousIndex < previousSize) { + if (fPrenotifiedDocumentListeners[previousIndex] != documentAdapter) + listeners[newIndex++] = fPrenotifiedDocumentListeners[previousIndex]; + previousIndex++; + } + fPrenotifiedDocumentListeners = listeners; + } + else { + fPrenotifiedDocumentListeners = null; + } + } + } + } + + /** + * This method is for INTERNAL USE ONLY and is NOT API. + * + * Rebuilds the StructuredDocumentRegion chain from the existing text. + * FileBuffer support does not allow clients to know the document's + * location before the text contents are set. + * + * @see set(String) + */ + public void reparse(Object requester) { + // check if we're already making document-wide changes on this thread + if (fStoppedCount > 0) + return; + + stopPostNotificationProcessing(); + clearReadOnly(); + + try { + acquireLock(); + + CharSequenceReader subSetTextStoreReader = new CharSequenceReader((CharSequence) getStore(), 0, getStore().getLength()); + resetParser(subSetTextStoreReader, 0); + // + setCachedDocumentRegion(getParser().getDocumentRegions()); + // when starting afresh, our cachedNode should be our firstNode, + // so be sure to initialize the firstNode and lastNode + initializeFirstAndLastDocumentRegion(); + StructuredDocumentRegionIterator.setParentDocument(getCachedDocumentRegion(), this); + } + finally { + releaseLock(); + } + + resumePostNotificationProcessing(); + } + + /** + * @see IDocument#replace + * @exception BadLocationException + * If position is not a valid range in the document + */ + public void replace(int offset, int length, String text) throws BadLocationException { + if (Debug.displayWarnings) { + System.out.println("Note: IStructuredDocument::replace(int, int, String) .... its better to use replaceText(source, string, int, int) API for structuredDocument updates"); //$NON-NLS-1$ + } + replaceText(this, offset, length, text); + } + + /** + * Replace the text with "newText" starting at position "start" for a + * length of "replaceLength". + * <p> + * + * @param pos + * start offset of text to replace None of the offsets include + * delimiters of preceeding lines. Offset 0 is the first + * character of the document. + * @param length + * start offset of text to replace + * @param text + * start offset of text to replace + * <p> + * Implementors have to notify TextChanged listeners after the + * content has been updated. The TextChangedEvent should be set + * as follows: + * + * event.type = SWT.TextReplaced event.start = start of the replaced text + * event.numReplacedLines = number of replaced lines event.numNewLines = + * number of new lines event.replacedLength = length of the replaced text + * event.newLength = length of the new text + * + * NOTE: numNewLines is the number of inserted lines and numReplacedLines + * is the number of deleted lines based on the change that occurs + * visually. For example: + * + * replacedText newText numReplacedLines numNewLines "" "\n" 0 1 "\n\n" + * "a" 2 0 "a" "\n\n" 0 2 + */ + /** + * One of the APIs to manipulate the IStructuredDocument in terms of text. + */ + public StructuredDocumentEvent replaceText(Object requester, int pos, int length, String text) { + if (length == 0 && (text == null || text.length() == 0)) + return replaceText(requester, pos, length, text, getModificationStamp(), true); + else + return replaceText(requester, pos, length, text, getNextModificationStamp(), true); + } + + public StructuredDocumentEvent replaceText(Object requester, int start, int replacementLength, String changes, boolean ignoreReadOnlySettings) { + long modificationStamp; + + if (replacementLength == 0 && (changes == null || changes.length() == 0)) + modificationStamp = getModificationStamp(); + else + modificationStamp = getNextModificationStamp(); + + return replaceText(requester, start, replacementLength, changes, modificationStamp, ignoreReadOnlySettings); + } + + private StructuredDocumentEvent replaceText(Object requester, int start, int replacementLength, String changes, long modificationStamp, boolean ignoreReadOnlySettings) { + StructuredDocumentEvent event = internalReplaceText(requester, start, replacementLength, changes, modificationStamp, ignoreReadOnlySettings); + return event; + } + + void resetParser(int startOffset, int endOffset) { + + RegionParser parser = getParser(); + ITextStore textStore = getStore(); + if (textStore instanceof CharSequence) { + CharSequenceReader subSetTextStoreReader = new CharSequenceReader((CharSequence) textStore, startOffset, endOffset - startOffset); + parser.reset(subSetTextStoreReader, startOffset); + } + else { + String newNodeText = get(startOffset, endOffset - startOffset); + parser.reset(newNodeText, startOffset); + + } + + } + + void resetParser(Reader reader, int startOffset) { + RegionParser parser = getParser(); + parser.reset(reader, startOffset); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing() + */ + public void resumePostNotificationProcessing() { + --fStoppedCount; + if (fStoppedCount == 0 && fReentranceCount == 0) + executePostNotificationChanges(); + } + + /** + * @deprecated in superclass in 3.0 - use a FindReplaceDocumentAdapter + * directly + * @see IDocument#search + */ + public int search(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException { + // (dmw) I added this warning, to know if still being used. I'm not + // sure it + // works as expected any longer. + // but the warning should be removed, once know. + Logger.log(Logger.INFO, "WARNING: using unsupported deprecated method 'search'"); //$NON-NLS-1$ + int offset = -1; + IRegion match = new FindReplaceDocumentAdapter(this).find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false); + if (match != null) { + offset = match.getOffset(); + } + return offset; + } + + /** + * @see IDocument#setText + */ + public void set(String string) { + if (Debug.displayInfo) { + System.out.println("Note: IStructuredDocument::setText(String) .... its better to use setText(source, string) API for structuredDocument updates"); //$NON-NLS-1$ + } + setText(null, string); + } + + /** + * This may be marked public, but should be packaged protected, once + * refactoring is complete (in other words, not for client use). + */ + public void setCachedDocumentRegion(IStructuredDocumentRegion structuredRegion) { + if (USE_LOCAL_THREAD) { + fCurrentDocumentRegionCache.set(structuredRegion); + } + else { + cachedDocumentRegion = structuredRegion; + } + } + + /** + * Sets the document's partitioner. + * + * @see IDocumentPartitioner + */ + public void setDocumentPartitioner(IDocumentPartitioner partitioner) { + setDocumentPartitioner(IDocumentExtension3.DEFAULT_PARTITIONING, partitioner); + } + + + public void setDocumentPartitioner(String partitioning, IDocumentPartitioner partitioner) { + if (partitioner == null) { + if (fDocumentPartitioners != null) { + fDocumentPartitioners.remove(partitioning); + if (fDocumentPartitioners.size() == 0) + fDocumentPartitioners = null; + } + } + else { + if (fDocumentPartitioners == null) + fDocumentPartitioners = new HashMap(); + fDocumentPartitioners.put(partitioning, partitioner); + } + DocumentPartitioningChangedEvent event = new DocumentPartitioningChangedEvent(this); + event.setPartitionChange(partitioning, 0, getLength()); + fireDocumentPartitioningChanged(event); + } + + public void setEncodingMemento(EncodingMemento encodingMemento) { + this.encodingMemento = encodingMemento; + } + + void setFirstDocumentRegion(IStructuredDocumentRegion region) { + firstDocumentRegion = region; + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#setInitialLineDelimiter(java.lang.String) + */ + public void setInitialLineDelimiter(String lineDelimiter) { + // make sure our preferred delimiter is + // one of the legal ones + if (Utilities.containsString(getLegalLineDelimiters(), lineDelimiter)) { + fInitialLineDelimiter= lineDelimiter; + } + else { + if (Logger.DEBUG_DOCUMENT) + Logger.log(Logger.INFO, "Attempt to set linedelimiter to non-legal delimiter"); //$NON-NLS-1$ //$NON-NLS-2$ + fInitialLineDelimiter = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, System.getProperty("line.separator"), new IScopeContext[] { new InstanceScope() });//$NON-NLS-1$ + } + } + + void setLastDocumentRegion(IStructuredDocumentRegion region) { + lastDocumentRegion = region; + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument#setLineDelimiter(java.lang.String) + */ + public void setLineDelimiter(String delimiter) { + setInitialLineDelimiter(delimiter); + } + + /** + * Sets the document's line tracker. Must be called at the beginning of + * the constructor. + * + * @param tracker + * the document's line tracker + */ + private void setLineTracker(ILineTracker tracker) { + Assert.isNotNull(tracker); + fTracker = tracker; + } + + public void setParser(RegionParser newParser) { + internal_setParser(newParser); + } + + /** + * @param positionManager + * The positionManager to set. + */ + // TODO: make private is needed, else remove + void setPositionManager(GenericPositionManager positionManager) { + fPositionManager = positionManager; + } + + /** + * + */ + public void setReParser(IStructuredTextReParser newReParser) { + fReParser = newReParser; + if (fReParser != null) { + fReParser.setStructuredDocument(this); + } + } + + /** + * One of the APIs to manipulate the IStructuredDocument in terms of text. + */ + public StructuredDocumentEvent setText(Object requester, String theString) { + StructuredDocumentEvent result = null; + result = replaceText(requester, 0, getLength(), theString, getNextModificationStamp(), true); + return result; + } + + /** + * Sets the document's text store. Must be called at the beginning of the + * constructor. + * + * @param store + * the document's text store + */ + private void setTextStore(ITextStore store) { + Assert.isNotNull(store); + fStore = store; + } + + public void setUndoManager(IStructuredTextUndoManager undoManager) { + + // if the undo manager has already been set, then + // fail fast, since changing the undo manager will lead + // to unusual results (or at least loss of undo stack). + if (fUndoManager != null && fUndoManager != undoManager) { + throw new IllegalArgumentException("can not change undo manager once its been set"); //$NON-NLS-1$ + } + else { + fUndoManager = undoManager; + } + } + + + /* + * {@inheritDoc} + */ + public void startSequentialRewrite(boolean normalized) { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing() + */ + public void stopPostNotificationProcessing() { + ++fStoppedCount; + } + + + /* + * {@inheritDoc} + */ + public void stopSequentialRewrite() { + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#subSequence(int, int) + */ + public CharSequence subSequence(int arg0, int arg1) { + return get(arg0, arg1); + } + + /** + * @param result + */ + private void updateDeletedFields(StructuredDocumentRegionsReplacedEvent event) { + IStructuredDocumentRegionList oldRegions = event.getOldStructuredDocumentRegions(); + for (int i = 0; i < oldRegions.getLength(); i++) { + IStructuredDocumentRegion structuredDocumentRegion = oldRegions.item(i); + structuredDocumentRegion.setDeleted(true); + } + + } + + /** + * Called by re-parser. Note: this method may be "public" but should only + * be called by re-parsers in the right circumstances. + */ + public void updateDocumentData(int start, int lengthToReplace, String changes) { + stopPostNotificationProcessing(); + getStore().replace(start, lengthToReplace, changes); + try { + getTracker().replace(start, lengthToReplace, changes); + } + + catch (BadLocationException e) { + // should be impossible here, but will log for now + Logger.logException(e); + } + if (fPositionManager != null) { + fPositionManager.updatePositions(new DocumentEvent(this, start, lengthToReplace, changes)); + } + fModificationStamp++; + fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp); + resumePostNotificationProcessing(); + } + + private StructuredDocumentEvent updateModel(Object requester, int start, int lengthToReplace, String changes) { + StructuredDocumentEvent result = null; + IStructuredTextReParser reParser = getReParser(); + // initialize the IStructuredTextReParser with the standard data + // that's + // always needed + reParser.initialize(requester, start, lengthToReplace, changes); + result = reParser.reparse(); + // if result is null at this point, then there must be an error, since + // even if there + // was no change (either disallow due to readonly, or a person pasted + // the same thing + // they had selected) then a "NoChange" event should have been fired. + Assert.isNotNull(result, "no structuredDocument event was created in IStructuredDocument::updateStructuredDocument"); //$NON-NLS-1$ + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument#getPreferredLineDelimiter() + */ + public String getPreferredLineDelimiter() { + return getDefaultLineDelimiter(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument#setPreferredLineDelimiter(java.lang.String) + */ + public void setPreferredLineDelimiter(String probableLineDelimiter) { + setInitialLineDelimiter(probableLineDelimiter); + + } + + + /** + * Class which implements the rewritable session for the SSE. + * + */ + static class StructuredDocumentRewriteSession extends DocumentRewriteSession { + + /** + * Creates a new session. + * + * @param sessionType + * the type of this session + */ + protected StructuredDocumentRewriteSession(DocumentRewriteSessionType sessionType) { + super(sessionType); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#startRewriteSession(org.eclipse.jface.text.DocumentRewriteSessionType) + */ + public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) throws IllegalStateException { + // delegate to sub-class, so UI threading is handled correctly + return internalStartRewriteSession(sessionType); + } + + /** + * NOT-API. Final protected so clients may call this method if needed, but + * cannot override. + * + * @param sessionType + * @return + * @throws IllegalStateException + */ + final protected DocumentRewriteSession internalStartRewriteSession(DocumentRewriteSessionType sessionType) throws IllegalStateException { + if (getActiveRewriteSession() != null) + throw new IllegalStateException("already in a rewrite session"); + + DocumentRewriteSession session = new StructuredDocumentRewriteSession(sessionType); + DocumentRewriteSessionEvent event = new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_START); + fireDocumentRewriteSessionEvent(event); + + ILineTracker tracker = getTracker(); + if (tracker instanceof ILineTrackerExtension) { + ILineTrackerExtension extension = (ILineTrackerExtension) tracker; + extension.startRewriteSession(session); + } + + startRewriteSessionOnPartitioners(session); + + if (DocumentRewriteSessionType.SEQUENTIAL == sessionType) + startSequentialRewrite(false); + else if (DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType) + startSequentialRewrite(true); + + fActiveRewriteSession = session; + return session; + } + + /** + * Starts the given rewrite session. + * + * @param session the rewrite session + * @since 2.0 + */ + final void startRewriteSessionOnPartitioners(DocumentRewriteSession session) { + if (fDocumentPartitioners != null) { + Iterator e= fDocumentPartitioners.values().iterator(); + while (e.hasNext()) { + Object partitioner= e.next(); + if (partitioner instanceof IDocumentPartitionerExtension3) { + IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner; + extension.startRewriteSession(session); + } + } + } + } + + + public void stopRewriteSession(DocumentRewriteSession session) { + // delegate to sub-class, so UI threading is handled correctly + internalStopRewriteSession(session); + } + + /** + * NOT-API. Final protected so clients may call this method if needed, but + * cannot override. + * + * @param session + */ + final protected void internalStopRewriteSession(DocumentRewriteSession session) { + if (fActiveRewriteSession == session) { + DocumentRewriteSessionType sessionType = session.getSessionType(); + if (DocumentRewriteSessionType.SEQUENTIAL == sessionType || DocumentRewriteSessionType.STRICTLY_SEQUENTIAL == sessionType) + stopSequentialRewrite(); + + stopRewriteSessionOnPartitioners(session); + + ILineTracker tracker = getTracker(); + if (tracker instanceof ILineTrackerExtension) { + ILineTrackerExtension extension = (ILineTrackerExtension) tracker; + extension.stopRewriteSession(session, get()); + } + + fActiveRewriteSession = null; + DocumentRewriteSessionEvent event = new DocumentRewriteSessionEvent(this, session, DocumentRewriteSessionEvent.SESSION_STOP); + fireDocumentRewriteSessionEvent(event); + } + } + + /** + * Stops the given rewrite session. + * + * @param session the rewrite session + * @since 2.0 + */ + final void stopRewriteSessionOnPartitioners(DocumentRewriteSession session) { + if (fDocumentPartitioners != null) { + DocumentPartitioningChangedEvent event= new DocumentPartitioningChangedEvent(this); + Iterator e= fDocumentPartitioners.keySet().iterator(); + while (e.hasNext()) { + String partitioning= (String) e.next(); + IDocumentPartitioner partitioner= (IDocumentPartitioner) fDocumentPartitioners.get(partitioning); + if (partitioner instanceof IDocumentPartitionerExtension3) { + IDocumentPartitionerExtension3 extension= (IDocumentPartitionerExtension3) partitioner; + extension.stopRewriteSession(session); + event.setPartitionChange(partitioning, 0, getLength()); + } + } + if (!event.isEmpty()) + fireDocumentPartitioningChanged(event); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#getActiveRewriteSession() + */ + public DocumentRewriteSession getActiveRewriteSession() { + return fActiveRewriteSession; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#addDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener) + */ + public void addDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) { + synchronized (listenerLock) { + Assert.isNotNull(listener); + if (fDocumentRewriteSessionListeners == null) { + fDocumentRewriteSessionListeners = new ArrayList(1); + } + if (!fDocumentRewriteSessionListeners.contains(listener)) + fDocumentRewriteSessionListeners.add(listener); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#removeDocumentRewriteSessionListener(org.eclipse.jface.text.IDocumentRewriteSessionListener) + */ + public void removeDocumentRewriteSessionListener(IDocumentRewriteSessionListener listener) { + synchronized (listenerLock) { + + Assert.isNotNull(listener); + if (fDocumentRewriteSessionListeners != null) + fDocumentRewriteSessionListeners.remove(listener); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#replace(int, int, + * java.lang.String, long) + */ + public void replace(int offset, int length, String text, long modificationStamp) throws BadLocationException { + replaceText(this, offset, length, text, modificationStamp, true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#set(java.lang.String, + * long) + */ + public void set(String text, long modificationStamp) { + // bug 151069 - overwrite read only regions when setting entire document + replaceText(null, 0, getLength(), text, modificationStamp, true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension4#getModificationStamp() + */ + public long getModificationStamp() { + return fModificationStamp; + } + + private long getNextModificationStamp() { + if (fNextModificationStamp == Long.MAX_VALUE || fNextModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) + fNextModificationStamp= 0; + else + fNextModificationStamp= fNextModificationStamp + 1; + + return fNextModificationStamp; + } + + /** + * Fires an event, as specified, to the associated listeners. + * + * @param event + * The event to fire, either a start or stop event. + */ + private void fireDocumentRewriteSessionEvent(final DocumentRewriteSessionEvent event) { + if (fDocumentRewriteSessionListeners == null || fDocumentRewriteSessionListeners.size() == 0) + return; + + Object[] listeners = fDocumentRewriteSessionListeners.toArray(); + for (int i = 0; i < listeners.length; i++) { + final IDocumentRewriteSessionListener l = (IDocumentRewriteSessionListener) listeners[i]; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + l.documentRewriteSessionChanged(event); + } + public void handleException(Throwable exception) { + // logged for us + } + }); + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocumentRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocumentRegion.java new file mode 100644 index 0000000..7fba9b3 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/BasicStructuredDocumentRegion.java
@@ -0,0 +1,622 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300430 - String concatenation + * David Carver (Intalio) - bug 300427 - Comparison of String Objects == or != + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + + + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.eclipse.wst.sse.core.internal.util.Assert; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.internal.util.Utilities; + + +public class BasicStructuredDocumentRegion implements IStructuredDocumentRegion { + private static final String TEXT_STORE_NOT_ASSIGNED = "text store not assigned yet"; //$NON-NLS-1$ + private static final String UNDEFINED = "org.eclipse.wst.sse.core.structuredDocument.UNDEFINED"; //$NON-NLS-1$ + + private ITextRegionList _regions; + /** + * has this region been removed from its document + */ + private static final byte MASK_IS_DELETED = 1; + /** + * was this region terminated normally + */ + private static final byte MASK_IS_ENDED = 1 << 1; + + private byte fIsDeletedOrEnded = 0; + + /** + * allow a pointer back to this nodes model + */ + private IStructuredDocument fParentDocument; + + protected int fLength; + private IStructuredDocumentRegion next = null; + private IStructuredDocumentRegion previous = null; + protected int start; + + public BasicStructuredDocumentRegion() { + super(); + _regions = new TextRegionListImpl(); + + } + + /** + * Even inside-this class uses of 'regions' should use this method, as + * this is where (soft) memory management/reparsing, etc., will be + * centralized. + */ + private ITextRegionList _getRegions() { + + return _regions; + } + + public void addRegion(ITextRegion aRegion) { + _getRegions().add(aRegion); + } + + public void adjust(int i) { + start += i; + } + + public void adjustLength(int i) { + fLength += i; + } + + public void adjustStart(int i) { + start += i; + } + + public void adjustTextLength(int i) { + // not supported + + } + + public boolean containsOffset(int i) { + + return getStartOffset() <= i && i < getEndOffset(); + } + + public boolean containsOffset(ITextRegion containedRegion, int offset) { + return getStartOffset(containedRegion) <= offset && offset < getEndOffset(containedRegion); + } + + public void equatePositions(ITextRegion region) { + start = region.getStart(); + fLength = region.getLength(); + } + + /** + * getEnd and getEndOffset are the same only for + * IStructuredDocumentRegions + */ + public int getEnd() { + return start + fLength; + } + + /** + * getEnd and getEndOffset are the same only for + * IStructuredDocumentRegions + */ + public int getEndOffset() { + return getEnd(); + } + + public int getEndOffset(ITextRegion containedRegion) { + return getStartOffset(containedRegion) + containedRegion.getLength(); + } + + public ITextRegion getFirstRegion() { + if (_getRegions() == null) + return null; + return _getRegions().get(0); + } + + public String getFullText() { + String result = ""; //$NON-NLS-1$ + try { + result = getParentDocument().get(start, fLength); + } + catch (BadLocationException e) { + // log for now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + return result; + } + + public String getFullText(ITextRegion aRegion) { + String result = ""; //$NON-NLS-1$ + try { + int regionStart = aRegion.getStart(); + int regionLength = aRegion.getLength(); + result = fParentDocument.get(start + regionStart, regionLength); + } + catch (BadLocationException e) { + // log for now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + return result; + } + + public String getFullText(String context) { + // DMW: looping is faster than enumeration, + // so switched around 2/12/03 + // Enumeration e = getRegions().elements(); + ITextRegion region = null; + String result = ""; //$NON-NLS-1$ + int length = getRegions().size(); + StringBuffer sb = new StringBuffer(result); + for (int i = 0; i < length; i++) { + region = getRegions().get(i); + if (region.getType().equals(context)) + sb.append(getFullText(region)); + } + result = sb.toString(); + return result; + } + + public ITextRegion getLastRegion() { + if (_getRegions() == null) + return null; + return _getRegions().get(_getRegions().size() - 1); + } + + public int getLength() { + return fLength; + } + + public IStructuredDocumentRegion getNext() { + return next; + } + + public int getNumberOfRegions() { + return _getRegions().size(); + } + + public IStructuredDocument getParentDocument() { + + return fParentDocument; + } + + public IStructuredDocumentRegion getPrevious() { + return previous; + } + + /** + * The parameter offset refers to the overall offset in the document. + */ + public ITextRegion getRegionAtCharacterOffset(int offset) { + if (_getRegions() != null) { + int thisStartOffset = getStartOffset(); + if (offset < thisStartOffset) + return null; + int thisEndOffset = getStartOffset() + getLength(); + if (offset > thisEndOffset) + return null; + // transform the requested offset to the "scale" that + // regions are stored in, which are all relative to the + // start point. + // int transformedOffset = offset - getStartOffset(); + // + ITextRegionList regions = getRegions(); + int length = regions.size(); + int low = 0; + int high = length; + int mid = 0; + // Binary search for the region + while (low < high) { + mid = low + ((high - low) >> 1); + ITextRegion region = regions.get(mid); + if (Debug.debugStructuredDocument) { + System.out.println("region(s) in IStructuredDocumentRegion::getRegionAtCharacterOffset: " + region); //$NON-NLS-1$ + System.out.println(" requested offset: " + offset); //$NON-NLS-1$ + // System.out.println(" transformedOffset: " + + // transformedOffset); //$NON-NLS-1$ + System.out.println(" region start: " + region.getStart()); //$NON-NLS-1$ + System.out.println(" region end: " + region.getEnd()); //$NON-NLS-1$ + System.out.println(" region type: " + region.getType()); //$NON-NLS-1$ + System.out.println(" region class: " + region.getClass()); //$NON-NLS-1$ + + } + // Region is before this one + if (offset < region.getStart() + thisStartOffset) + high = mid; + else if (offset > (region.getEnd() + thisStartOffset - 1)) + low = mid + 1; + else + return region; + } + } + return null; + } + + public ITextRegionList getRegions() { + return _getRegions(); + } + + /** + * getStart and getStartOffset are the same only for + * IStrucutredDocumentRegions + */ + public int getStart() { + return start; + } + + /** + * getStart and getStartOffset are the same only for + * IStrucutredDocumentRegions + */ + public int getStartOffset() { + return getStart(); + } + + public int getStartOffset(ITextRegion containedRegion) { + // assert: containedRegion can not be null + // (might be performance hit if literally put in assert call, + // but containedRegion can not be null). Needs to be checked + // by calling code. + return getStartOffset() + containedRegion.getStart(); + } + + public String getText() { + String result = null; + try { + if (fParentDocument == null) { + // likely to happen during inspecting + result = TEXT_STORE_NOT_ASSIGNED; + } + else { + result = fParentDocument.get(start, fLength); + } + } + catch (BadLocationException e) { + // log for now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + return result; + } + + public String getText(ITextRegion aRegion) { + // assert: aRegion can not be null + // (might be performance hit if literally put in assert call, + // but aRegion can not be null). Needs to be checked + // by calling code. + try { + return fParentDocument.get(this.getStartOffset(aRegion), aRegion.getTextLength()); + } + catch (BadLocationException e) { + Logger.logException(e); + } + return ""; //$NON-NLS-1$ + } + + /** + * Returns the text of the first region with the matching context type + */ + public String getText(String context) { + // DMW: looping is faster than enumeration, + // so switched around 2/12/03 + // Enumeration e = getRegions().elements(); + ITextRegion region = null; + String result = ""; //$NON-NLS-1$ + int length = getRegions().size(); + for (int i = 0; i < length; i++) { + region = getRegions().get(i); + if (region.getType().equals(context)) { + result = getText(region); + break; + } + } + return result; + } + + public int getTextEnd() { + return start + fLength; + } + + /** + * @return int + */ + public int getTextEndOffset() { + ITextRegion region = _getRegions().get(_getRegions().size() - 1); + return getStartOffset() + region.getTextEnd(); + } + + public int getTextEndOffset(ITextRegion containedRegion) { + return getStartOffset(containedRegion) + containedRegion.getTextLength(); + } + + public int getTextLength() { + return fLength; + } + + /** + * Provides the type of IStructuredDocumentRegion ... not to be confused + * with type of XML node! This is subclassed, if something other than type + * of first region is desired. + * + */ + public String getType() { + String result = UNDEFINED; + ITextRegionList subregions = getRegions(); + if (subregions != null && subregions.size() > 0) { + ITextRegion firstRegion = subregions.get(0); + if (firstRegion != null) { + result = firstRegion.getType(); + } + } + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.text.IStructuredDocumentRegion#isDeleted() + */ + public boolean isDeleted() { + return (fIsDeletedOrEnded & MASK_IS_DELETED) != 0 || (fParentDocument == null); + } + + /** + * + * @return boolean + */ + public boolean isEnded() { + return (fIsDeletedOrEnded & MASK_IS_ENDED) != 0; + } + + public boolean sameAs(IStructuredDocumentRegion region, int shift) { + boolean result = false; + // if region == null, we return false; + if (region != null) { + // if the regions are the same instance, they are equal + if (this == region) { + result = true; + } + else { + // this is the non-trivial part + // note: we change for type first, then start offset and end + // offset, + // since that would decide many cases right away and avoid the + // text comparison + if (getType().equals(region.getType())) { + if (sameOffsetsAs(region, shift) && sameTextAs(region, shift)) { + result = true; + } + } + + } + } + return result; + } + + public boolean sameAs(ITextRegion oldRegion, IStructuredDocumentRegion newDocumentRegion, ITextRegion newRegion, int shift) { + boolean result = false; + // if any region is null, we return false (even if both are!) + if ((oldRegion != null) && (newRegion != null)) { + // if the regions are the same instance, they are equal + if (oldRegion == newRegion) { + result = true; + } + else { + // this is the non-trivial part + // note: we change for type first, then start offset and end + // offset, + // since that would decide many cases right away and avoid the + // text comparison + if (oldRegion.getType().equals(newRegion.getType())) { + if (sameOffsetsAs(oldRegion, newDocumentRegion, newRegion, shift)) { + if (sameTextAs(oldRegion, newDocumentRegion, newRegion, shift)) { + result = true; + } + } + } + } + + } + + return result; + } + + private boolean sameOffsetsAs(IStructuredDocumentRegion region, int shift) { + if (getStartOffset() == region.getStartOffset() - shift) { + if (getEndOffset() == region.getEndOffset() - shift) { + return true; + } + } + return false; + } + + private boolean sameOffsetsAs(ITextRegion oldRegion, IStructuredDocumentRegion documentRegion, ITextRegion newRegion, int shift) { + if (getStartOffset(oldRegion) == documentRegion.getStartOffset(newRegion) - shift) { + if (getEndOffset(oldRegion) == documentRegion.getEndOffset(newRegion) - shift) { + return true; + } + } + return false; + } + + private boolean sameTextAs(IStructuredDocumentRegion region, int shift) { + boolean result = false; + try { + if (getText().equals(region.getText())) { + result = true; + } + } + // ISSUE: we should not need this + catch (StringIndexOutOfBoundsException e) { + result = false; + } + + return result; + } + + private boolean sameTextAs(ITextRegion oldRegion, IStructuredDocumentRegion documentRegion, ITextRegion newRegion, int shift) { + boolean result = false; + + if (getText(oldRegion).equals(documentRegion.getText(newRegion))) { + result = true; + } + + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.text.IStructuredDocumentRegion#setDelete(boolean) + */ + public void setDeleted(boolean isDeleted) { + if (isDeleted) + fIsDeletedOrEnded |= MASK_IS_DELETED; + else + fIsDeletedOrEnded &= ~MASK_IS_DELETED; + } + + /** + * + * @param newHasEnd + * boolean + */ + public void setEnded(boolean newHasEnd) { + if (newHasEnd) + fIsDeletedOrEnded |= MASK_IS_ENDED; + else + fIsDeletedOrEnded &= ~MASK_IS_ENDED; + } + + public void setLength(int newLength) { + // textLength = newLength; + fLength = newLength; + } + + public void setNext(IStructuredDocumentRegion newNext) { + next = newNext; + } + + public void setParentDocument(IStructuredDocument document) { + fParentDocument = document; + + } + + public void setPrevious(IStructuredDocumentRegion newPrevious) { + previous = newPrevious; + } + + public void setRegions(ITextRegionList containedRegions) { + _regions = containedRegions; + } + + public void setStart(int newStart) { + start = newStart; + } + + public String toString() { + // NOTE: if the document held by any region has been updated and the + // region offsets have not + // yet been updated, the output from this method invalid. + // Also note, this method can not be changed, without "breaking" + // unit tests, since some of them compare current results to previous + // results. + String result = null; + result = "[" + getStart() + ", " + getEnd() + "] (" + getText() + ")"; //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + return result; + } + + private void updateDownStreamRegions(ITextRegion changedRegion, int lengthDifference) { + int listLength = _getRegions().size(); + int startIndex = 0; + // first, loop through to find index of where to start + for (int i = 0; i < listLength; i++) { + ITextRegion region = _getRegions().get(i); + if (region == changedRegion) { + startIndex = i; + break; + } + } + // now, beginning one past the one that was changed, loop + // through to end of list, adjusting the start postions. + startIndex++; + for (int j = startIndex; j < listLength; j++) { + ITextRegion region = _getRegions().get(j); + region.adjustStart(lengthDifference); + } + } + + public StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion structuredDocumentRegion, String changes, int requestStart, int lengthToReplace) { + StructuredDocumentEvent result = null; + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Get the region pointed to by the requestStart postion, and give + // that region a chance to effect + // the update. + ITextRegion region = getRegionAtCharacterOffset(requestStart); + // if there is no region, then the requested changes must come right + // after the + // node (and right after the last region). This happens, for example, + // when someone + // types something at the end of the document, or more commonly, when + // they are right + // at the beginning of one node, and the dirty start is therefore + // calculated to be the + // previous node. + // So, in this case, we'll give the last region a chance to see if it + // wants to + // swallow the requested changes -- but only for inserts -- deletes + // and "replaces" + // should be reparsed if they are in these border regions, and only if + // the + if ((region == null) && (lengthToReplace == 0)) { + region = _getRegions().get(_getRegions().size() - 1); + // make sure the region is contiguous + if (getEndOffset(region) == requestStart) { + result = region.updateRegion(requester, this, changes, requestStart, lengthToReplace); + } + } + else { + if (region != null) { + // + // If the requested change spans more than one region, then + // we don't give the region a chance to update. + if ((containsOffset(region, requestStart)) && (containsOffset(region, requestStart + lengthToReplace))) { + result = region.updateRegion(requester, this, changes, requestStart, lengthToReplace); + } + } + } + // if result is not null, then we need to update the start and end + // postions of the regions that follow this one + // if result is null, then apply the flatnode specific checks on what + // it can change + // (i.e. more than one region, but no change to the node itself) + if (result != null) { + // That is, a region decided it could handle the change and + // created + // a region changed event. + Assert.isTrue(result instanceof RegionChangedEvent, "Program Error"); //$NON-NLS-1$ + updateDownStreamRegions(((RegionChangedEvent) result).getRegion(), lengthDifference); + // PLUS, we need to update our own node end point (length) + setLength(getLength() + lengthDifference); + } + + return result; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/CharSequenceReader.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/CharSequenceReader.java new file mode 100644 index 0000000..6ab29b5 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/CharSequenceReader.java
@@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import java.io.IOException; +import java.io.Reader; + +public class CharSequenceReader extends Reader { + private int fCurrentPosition; + private int fMaximumReadOffset; + + private CharSequence fOriginalSource; + + /** + * + */ + CharSequenceReader() { + super(); + } + + + public CharSequenceReader(CharSequence originalSource, int offset, int length) { + // ISSUE: should we "fail fast" if requested length is more than there + // is? + fOriginalSource = originalSource; + int startOffset = offset; + int maxRequestedOffset = startOffset + length; + int maxPossibleOffset = 0 + originalSource.length(); + fMaximumReadOffset = Math.min(maxRequestedOffset, maxPossibleOffset); + + fCurrentPosition = startOffset; + + } + + /** + * @param lockObject + */ + CharSequenceReader(Object lockObject) { + super(lockObject); + // for thread safety, may need to add back locking mechanism + // in our custom constructor. This constructor left here just + // for a reminder. + } + + public void close() throws IOException { + // nothing to do when we close + // (may be to eventually "unlock" or null out some varibles + // just for hygene. + // or, perhaps if already closed once throw IOException? for + // consistency? + } + + /** + * @return Returns the originalSource. + * @deprecated - only temporarily public, should be 'default' eventually + * or go away altogether. + */ + public CharSequence getOriginalSource() { + return fOriginalSource; + } + + public int read() { + int result = -1; + if (fCurrentPosition < fMaximumReadOffset) { + result = fOriginalSource.charAt(fCurrentPosition++); + } + return result; + } + + /** + * Read characters into a portion of an array. This method will block + * until some input is available, an I/O error occurs, or the end of the + * stream is reached. + * + * @param cbuf + * Destination buffer + * @param off + * Offset at which to start storing characters + * @param len + * Maximum number of characters to read + * + * @return The number of characters read, or -1 if the end of the stream + * has been reached + * + * @exception IOException + * If an I/O error occurs + */ + + public int read(char[] cbuf, int off, int len) throws IOException { + int charsToRead = -1; + // if already over max, just return -1 + // remember, currentPosition is what is getting ready to be read + // (that is, its already been incremented in read()). + if (fCurrentPosition < fMaximumReadOffset) { + + + int buffMaxToRead = cbuf.length - off; + int minRequested = Math.min(buffMaxToRead, len); + int lengthRemaining = fMaximumReadOffset - fCurrentPosition; + charsToRead = Math.min(minRequested, lengthRemaining); + + + CharSequence seq = fOriginalSource.subSequence(fCurrentPosition, fCurrentPosition + charsToRead); + // for now, hard assumption that original is a String since source + // is assumed to be document, or text store + String seqString = (String) seq; + seqString.getChars(0, seqString.length(), cbuf, off); + + + + fCurrentPosition = fCurrentPosition + charsToRead; + + + } + return charsToRead; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/CoreNodeList.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/CoreNodeList.java new file mode 100644 index 0000000..6acd927 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/CoreNodeList.java
@@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + + + +import java.util.Enumeration; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; + + +public class CoreNodeList implements IStructuredDocumentRegionList { + int countedLength; + int currentIndex = -1; + + IStructuredDocumentRegion[] flatNodes; + IStructuredDocumentRegion head; + + /** + * CoreNodeList constructor comment. + */ + public CoreNodeList() { + super(); + // create an array, even if zero length + flatNodes = new IStructuredDocumentRegion[0]; + } + + public CoreNodeList(IStructuredDocumentRegion newHead) { + super(); + // save head + head = newHead; + int count = 0; + IStructuredDocumentRegion countNode = newHead; + // we have to go through the list once, to get its + // length in order to create the array + while (countNode != null) { + count++; + countNode = countNode.getNext(); + } + // create an array, even if zero length + flatNodes = new IStructuredDocumentRegion[count]; + // start countNode over again, so to speak. + countNode = newHead; + count = 0; + while (countNode != null) { + flatNodes[count++] = countNode; + countNode = countNode.getNext(); + } + if (count > 0) { + currentIndex = 0; + // else it stays at -1 initialized at object creation + // + // save length + countedLength = count; + } + } + + public CoreNodeList(IStructuredDocumentRegion start, IStructuredDocumentRegion end) { + super(); + // save head + head = start; + int count = 0; + IStructuredDocumentRegion countNode = start; + if ((start == null) || (end == null)) { + // error condition + //throw new IllegalArgumentException("Must provide start and end + // nodes to construct CoreNodeList"); + } else { + count = 1; + while ((countNode != null) && (countNode != end)) { + count++; + countNode = countNode.getNext(); + } + } + // if we ended because the last one was null, + // backup one. + if (countNode == null) + count--; + if (count < 0) { + count = 0; + } + flatNodes = new IStructuredDocumentRegion[count]; + if (count > 0) { + flatNodes[0] = countNode = start; + for (int i = 1; i < count; i++) { + flatNodes[i] = flatNodes[i - 1].getNext(); + } + + } + currentIndex = 0; + countedLength = count; + } + + public Enumeration elements() { + StructuredDocumentRegionEnumeration result = null; + if ((flatNodes != null) && (flatNodes.length > 0)) + result = new StructuredDocumentRegionEnumeration(flatNodes[0], flatNodes[flatNodes.length - 1]); + else + result = new StructuredDocumentRegionEnumeration(null); + return result; + } + + public int getLength() { + return flatNodes.length; + } + + public boolean includes(Object o) { + if (flatNodes == null) + return false; + for (int i = 0; i < flatNodes.length; i++) + if (flatNodes[i] == o) + return true; + return false; + } + + public IStructuredDocumentRegion item(int i) { + return flatNodes[i]; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/DeleteEqualPositionUpdater.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/DeleteEqualPositionUpdater.java new file mode 100644 index 0000000..e33783e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/DeleteEqualPositionUpdater.java
@@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.DefaultPositionUpdater; + +/** + * Follows the behavior of DefaultPositionUpdater except in addition to + * deleting/overwriting text which completely contains the position deletes + * the position, deleting text that equals the text in position also deletes + * the position. + * + * @see org.eclipse.jface.text.DefaultPositionUpdater + */ +public class DeleteEqualPositionUpdater extends DefaultPositionUpdater { + + /** + * @param category + */ + public DeleteEqualPositionUpdater(String category) { + super(category); + } + + /** + * Determines whether the currently investigated position has been deleted + * by the replace operation specified in the current event. If so, it + * deletes the position and removes it from the document's position + * category. + * + * NOTE: position is deleted if current event completely overwrites + * position OR if current event deletes the area surrounding/including the + * position + * + * @return <code>true</code> if position has been deleted + */ + protected boolean notDeleted() { + // position is deleted if current event completely overwrites position + // OR if + // current event deletes the area surrounding/including the position + if ((fOffset < fPosition.offset && (fPosition.offset + fPosition.length < fOffset + fLength)) || (fOffset <= fPosition.offset && (fPosition.offset + fPosition.length <= fOffset + fLength) && fReplaceLength == 0)) { + + fPosition.delete(); + + try { + fDocument.removePosition(getCategory(), fPosition); + } catch (BadPositionCategoryException x) { + } + + return false; + } + + return true; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/GenericPositionManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/GenericPositionManager.java new file mode 100644 index 0000000..0a422a3 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/GenericPositionManager.java
@@ -0,0 +1,409 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ + +package org.eclipse.wst.sse.core.internal.text; + +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.BadPositionCategoryException; +import org.eclipse.jface.text.DefaultPositionUpdater; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IPositionUpdater; +import org.eclipse.jface.text.Position; +import org.eclipse.wst.sse.core.internal.util.Assert; + +/** + * Based on the Position management methods from + * org.eclipse.jface.text.AbstractDocument + */ + +public class GenericPositionManager { + private CharSequence fCharSequence; + + + + private Map fPositions; + /** All registered document position updaters */ + private List fPositionUpdaters; + + /** + * don't allow instantiation with out document pointer + * + */ + private GenericPositionManager() { + super(); + } + + /** + * + */ + public GenericPositionManager(CharSequence charSequence) { + this(); + // we only use charSequence for "length", to + // made more generic than "document" even "text store" + fCharSequence = charSequence; + completeInitialization(); + } + + /* + * @see org.eclipse.jface.text.IDocument#addPosition(org.eclipse.jface.text.Position) + */ + public void addPosition(Position position) throws BadLocationException { + try { + addPosition(IDocument.DEFAULT_CATEGORY, position); + } + catch (BadPositionCategoryException e) { + } + } + + + + /* + * @see org.eclipse.jface.text.IDocument#addPosition(java.lang.String, + * org.eclipse.jface.text.Position) + */ + public synchronized void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException { + + if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getDocumentLength())) + throw new BadLocationException(); + + if (category == null) + throw new BadPositionCategoryException(); + + List list = (List) fPositions.get(category); + if (list == null) + throw new BadPositionCategoryException(); + + list.add(computeIndexInPositionList(list, position.offset), position); + } + + /* + * @see org.eclipse.jface.text.IDocument#addPositionCategory(java.lang.String) + */ + public void addPositionCategory(String category) { + + if (category == null) + return; + + if (!containsPositionCategory(category)) + fPositions.put(category, new ArrayList()); + } + + /* + * @see org.eclipse.jface.text.IDocument#addPositionUpdater(org.eclipse.jface.text.IPositionUpdater) + */ + public void addPositionUpdater(IPositionUpdater updater) { + insertPositionUpdater(updater, fPositionUpdaters.size()); + } + + + /** + * Initializes document listeners, positions, and position updaters. Must + * be called inside the constructor after the implementation plug-ins have + * been set. + */ + protected void completeInitialization() { + + fPositions = new HashMap(); + fPositionUpdaters = new ArrayList(); + + addPositionCategory(IDocument.DEFAULT_CATEGORY); + addPositionUpdater(new DefaultPositionUpdater(IDocument.DEFAULT_CATEGORY)); + } + + /* + * @see org.eclipse.jface.text.IDocument#computeIndexInCategory(java.lang.String, + * int) + */ + public int computeIndexInCategory(String category, int offset) throws BadLocationException, BadPositionCategoryException { + + if (0 > offset || offset > getDocumentLength()) + throw new BadLocationException(); + + List c = (List) fPositions.get(category); + if (c == null) + throw new BadPositionCategoryException(); + + return computeIndexInPositionList(c, offset); + } + + + /** + * Computes the index in the list of positions at which a position with + * the given offset would be inserted. The position is supposed to become + * the first in this list of all positions with the same offset. + * + * @param positions + * the list in which the index is computed + * @param offset + * the offset for which the index is computed + * @return the computed index + * + * @see IDocument#computeIndexInCategory(String, int) + */ + protected synchronized int computeIndexInPositionList(List positions, int offset) { + + if (positions.size() == 0) + return 0; + + int left = 0; + int right = positions.size() - 1; + int mid = 0; + Position p = null; + + while (left < right) { + + mid = (left + right) / 2; + + p = (Position) positions.get(mid); + if (offset < p.getOffset()) { + if (left == mid) + right = left; + else + right = mid - 1; + } + else if (offset > p.getOffset()) { + if (right == mid) + left = right; + else + left = mid + 1; + } + else if (offset == p.getOffset()) { + left = right = mid; + } + + } + + int pos = left; + p = (Position) positions.get(pos); + if (offset > p.getOffset()) { + // append to the end + pos++; + } + else { + // entry will became the first of all entries with the same + // offset + do { + --pos; + if (pos < 0) + break; + p = (Position) positions.get(pos); + } + while (offset == p.getOffset()); + ++pos; + } + + Assert.isTrue(0 <= pos && pos <= positions.size()); + + return pos; + } + + /* + * @see org.eclipse.jface.text.IDocument#containsPosition(java.lang.String, + * int, int) + */ + public boolean containsPosition(String category, int offset, int length) { + + if (category == null) + return false; + + List list = (List) fPositions.get(category); + if (list == null) + return false; + + int size = list.size(); + if (size == 0) + return false; + + int index = computeIndexInPositionList(list, offset); + if (index < size) { + Position p = (Position) list.get(index); + while (p != null && p.offset == offset) { + if (p.length == length) + return true; + ++index; + p = (index < size) ? (Position) list.get(index) : null; + } + } + + return false; + } + + /* + * @see org.eclipse.jface.text.IDocument#containsPositionCategory(java.lang.String) + */ + public boolean containsPositionCategory(String category) { + if (category != null) + return fPositions.containsKey(category); + return false; + } + + + + public int getDocumentLength() { + return fCharSequence.length(); + } + + /** + * Returns all positions managed by the document grouped by category. + * + * @return the document's positions + */ + protected Map getDocumentManagedPositions() { + return fPositions; + } + + /* + * @see org.eclipse.jface.text.IDocument#getPositionCategories() + */ + public String[] getPositionCategories() { + String[] categories = new String[fPositions.size()]; + Iterator keys = fPositions.keySet().iterator(); + for (int i = 0; i < categories.length; i++) + categories[i] = (String) keys.next(); + return categories; + } + + + public Position[] getPositions(String category) throws BadPositionCategoryException { + + if (category == null) + throw new BadPositionCategoryException(); + + List c = (List) fPositions.get(category); + if (c == null) + throw new BadPositionCategoryException(); + + Position[] positions = new Position[c.size()]; + c.toArray(positions); + return positions; + } + + /* + * @see org.eclipse.jface.text.IDocument#getPositionUpdaters() + */ + public IPositionUpdater[] getPositionUpdaters() { + IPositionUpdater[] updaters = new IPositionUpdater[fPositionUpdaters.size()]; + fPositionUpdaters.toArray(updaters); + return updaters; + } + + + + /* + * @see org.eclipse.jface.text.IDocument#insertPositionUpdater(org.eclipse.jface.text.IPositionUpdater, + * int) + */ + public synchronized void insertPositionUpdater(IPositionUpdater updater, int index) { + + for (int i = fPositionUpdaters.size() - 1; i >= 0; i--) { + if (fPositionUpdaters.get(i) == updater) + return; + } + + if (index == fPositionUpdaters.size()) + fPositionUpdaters.add(updater); + else + fPositionUpdaters.add(index, updater); + } + + /* + * @see org.eclipse.jface.text.IDocument#removePosition(org.eclipse.jface.text.Position) + */ + public void removePosition(Position position) { + try { + removePosition(IDocument.DEFAULT_CATEGORY, position); + } + catch (BadPositionCategoryException e) { + } + } + + /* + * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, + * org.eclipse.jface.text.Position) + */ + public synchronized void removePosition(String category, Position position) throws BadPositionCategoryException { + + if (position == null) + return; + + if (category == null) + throw new BadPositionCategoryException(); + + List c = (List) fPositions.get(category); + if (c == null) + throw new BadPositionCategoryException(); + + // remove based on identity not equality + int size = c.size(); + for (int i = 0; i < size; i++) { + if (position == c.get(i)) { + c.remove(i); + return; + } + } + } + + /* + * @see org.eclipse.jface.text.IDocument#removePositionCategory(java.lang.String) + */ + public void removePositionCategory(String category) throws BadPositionCategoryException { + + if (category == null) + return; + + if (!containsPositionCategory(category)) + throw new BadPositionCategoryException(); + + fPositions.remove(category); + } + + /* + * @see org.eclipse.jface.text.IDocument#removePositionUpdater(org.eclipse.jface.text.IPositionUpdater) + */ + public synchronized void removePositionUpdater(IPositionUpdater updater) { + for (int i = fPositionUpdaters.size() - 1; i >= 0; i--) { + if (fPositionUpdaters.get(i) == updater) { + fPositionUpdaters.remove(i); + return; + } + } + } + + + /** + * Updates all positions of all categories to the change described by the + * document event. All registered document updaters are called in the + * sequence they have been arranged. Uses a robust iterator. + * + * @param event + * the document event describing the change to which to adapt + * the positions + */ + protected synchronized void updatePositions(DocumentEvent event) { + List list = new ArrayList(fPositionUpdaters); + Iterator e = list.iterator(); + while (e.hasNext()) { + IPositionUpdater u = (IPositionUpdater) e.next(); + u.update(event); + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/IExecutionDelegatable.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/IExecutionDelegatable.java new file mode 100644 index 0000000..03ed267 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/IExecutionDelegatable.java
@@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import org.eclipse.wst.sse.core.internal.IExecutionDelegate; + +public interface IExecutionDelegatable { + + void setExecutionDelegate(IExecutionDelegate executionDelegate); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/IRegionComparible.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/IRegionComparible.java new file mode 100644 index 0000000..3230439 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/IRegionComparible.java
@@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +public interface IRegionComparible { + boolean regionMatches(int offset, int length, String stringToCompare); + + boolean regionMatchesIgnoreCase(int offset, int length, String stringToCompare); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/JobSafeStructuredDocument.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/JobSafeStructuredDocument.java new file mode 100644 index 0000000..502b508 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/JobSafeStructuredDocument.java
@@ -0,0 +1,249 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import java.util.Stack; + +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.jobs.ILock; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.wst.sse.core.internal.IExecutionDelegate; +import org.eclipse.wst.sse.core.internal.ILockable; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; + +/** + * An IStructuredDocument that performs most of its computation and event + * notification through an IExecutionDelegate. + * + * If the delegate has not been set, we execute on current thread, like + * "normal". This is the case for normal non-editor use (which should still, + * ultimately, be protected by a scheduling rule). For every operation, a + * runnable is created, even if later (in the execution delegate instance) it + * is decided nothing special is needed (that is, in fact being called from an + * editor's display thread, in which case its just executed) in the UI. + */ +public class JobSafeStructuredDocument extends BasicStructuredDocument implements IExecutionDelegatable, ILockable { + + private static abstract class JobSafeRunnable implements ISafeRunnable { + public void handleException(Throwable exception) { + // logged in SafeRunner + } + } + + private Stack fExecutionDelegates = new Stack(); + private ILock fLockable = Job.getJobManager().newLock(); + + public JobSafeStructuredDocument() { + super(); + } + + + public JobSafeStructuredDocument(RegionParser parser) { + super(parser); + } + + + /** + * + */ + protected final void acquireLock() { + getLockObject().acquire(); + } + + private IExecutionDelegate getExecutionDelegate() { + if (!fExecutionDelegates.isEmpty()) + return (IExecutionDelegate) fExecutionDelegates.peek(); + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.ILockable#getLock() + */ + + public ILock getLockObject() { + return fLockable; + } + + + /** + * + */ + protected final void releaseLock() { + getLockObject().release(); + } + + /* + * @see org.eclipse.jface.text.IDocument.replace(int, int, String) + */ + public void replace(final int offset, final int length, final String text) throws BadLocationException { + IExecutionDelegate delegate = getExecutionDelegate(); + if (delegate == null) { + super.replace(offset, length, text); + } + else { + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + JobSafeStructuredDocument.super.replace(offset, length, text); + } + }; + delegate.execute(runnable); + } + } + + /* + * @see org.eclipse.jface.text.IDocumentExtension4.replace(int, int, String, long) + */ + public void replace(final int offset, final int length, final String text, final long modificationStamp) throws BadLocationException { + IExecutionDelegate delegate = getExecutionDelegate(); + if (delegate == null) { + super.replace(offset, length, text, modificationStamp); + } + else { + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + JobSafeStructuredDocument.super.replace(offset, length, text, modificationStamp); + } + }; + delegate.execute(runnable); + } + } + + /* (non-Javadoc) + * @see org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument#replaceText(java.lang.Object, int, int, java.lang.String) + */ + public StructuredDocumentEvent replaceText(final Object requester, final int start, final int replacementLength, final String changes) { + StructuredDocumentEvent event = null; + IExecutionDelegate delegate = getExecutionDelegate(); + if (delegate == null) { + event = super.replaceText(requester, start, replacementLength, changes); + } + else { + final Object[] resultSlot = new Object[1]; + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + resultSlot[0] = JobSafeStructuredDocument.super.replaceText(requester, start, replacementLength, changes); + } + }; + delegate.execute(runnable); + event = (StructuredDocumentEvent) resultSlot[0]; + } + return event; + } + + /* (non-Javadoc) + * @see org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument#replaceText(java.lang.Object, int, int, java.lang.String, boolean) + */ + public StructuredDocumentEvent replaceText(final Object requester, final int start, final int replacementLength, final String changes, final boolean ignoreReadOnlySettings) { + StructuredDocumentEvent event = null; + IExecutionDelegate delegate = getExecutionDelegate(); + if (delegate == null) { + event = super.replaceText(requester, start, replacementLength, changes, ignoreReadOnlySettings); + } + else { + final Object[] resultSlot = new Object[1]; + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + resultSlot[0] = JobSafeStructuredDocument.super.replaceText(requester, start, replacementLength, changes, ignoreReadOnlySettings); + } + + public void handleException(Throwable exception) { + resultSlot[0] = new NoChangeEvent(JobSafeStructuredDocument.this, requester, changes, start, replacementLength); + super.handleException(exception); + } + }; + delegate.execute(runnable); + event = (StructuredDocumentEvent) resultSlot[0]; + } + return event; + } + + public void setExecutionDelegate(IExecutionDelegate delegate) { + if (delegate != null) + fExecutionDelegates.push(delegate); + else if (!fExecutionDelegates.isEmpty()) + fExecutionDelegates.pop(); + } + + public StructuredDocumentEvent setText(final Object requester, final String theString) { + StructuredDocumentEvent event = null; + IExecutionDelegate executionDelegate = getExecutionDelegate(); + if (executionDelegate == null) { + event = super.setText(requester, theString); + } + else { + final Object[] resultSlot = new Object[1]; + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + resultSlot[0] = JobSafeStructuredDocument.super.setText(requester, theString); + } + public void handleException(Throwable exception) { + resultSlot[0] = new NoChangeEvent(JobSafeStructuredDocument.this, requester, theString, 0, 0); + super.handleException(exception); + } + }; + executionDelegate.execute(runnable); + event = (StructuredDocumentEvent) resultSlot[0]; + } + return event; + } + + public DocumentRewriteSession startRewriteSession(DocumentRewriteSessionType sessionType) throws IllegalStateException { + DocumentRewriteSession session = null; + IExecutionDelegate executionDelegate = getExecutionDelegate(); + if (executionDelegate == null) { + session = internalStartRewriteSession(sessionType); + } + else { + final Object[] resultSlot = new Object[1]; + final DocumentRewriteSessionType finalSessionType = sessionType; + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + resultSlot[0] = internalStartRewriteSession(finalSessionType); + } + }; + executionDelegate.execute(runnable); + if (resultSlot[0] instanceof Throwable) { + throw new RuntimeException((Throwable) resultSlot[0]); + } + else { + session = (DocumentRewriteSession) resultSlot[0]; + } + } + return session; + } + + public void stopRewriteSession(DocumentRewriteSession session) { + IExecutionDelegate executionDelegate = getExecutionDelegate(); + if (executionDelegate == null) { + internalStopRewriteSession(session); + } + else { + final DocumentRewriteSession finalSession = session; + JobSafeRunnable runnable = new JobSafeRunnable() { + public void run() throws Exception { + internalStopRewriteSession(finalSession); + } + }; + executionDelegate.execute(runnable); + } + } + + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/MinimalDocument.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/MinimalDocument.java new file mode 100644 index 0000000..de3ff23 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/MinimalDocument.java
@@ -0,0 +1,445 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.jface.text.IDocumentPartitioningListener; +import org.eclipse.jface.text.IPositionUpdater; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.TypedRegion; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.NotImplementedException; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.provisional.events.IModelAboutToBeChangedListener; +import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextReParser; +import org.eclipse.wst.sse.core.internal.undo.IStructuredTextUndoManager; + + +/** + * Purely a dummy "marker" instance for StructuredDocumentRegions which are + * created temorarily in the course of re-parsing. Primarily a place holder, + * but can be needed to get text from. + */ +public class MinimalDocument implements IStructuredDocument { + private SubSetTextStore data; + + /** + * Marked private to be sure never created without data being initialized. + * + */ + private MinimalDocument() { + super(); + } + + public MinimalDocument(SubSetTextStore initialContents) { + this(); + data = initialContents; + } + + public void addDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addDocumentChangedListener(IStructuredDocumentListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addDocumentChangingListener(IStructuredDocumentListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addDocumentListener(IDocumentListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addPosition(Position position) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addPositionCategory(String category) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addPositionUpdater(IPositionUpdater updater) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void addPrenotifiedDocumentListener(IDocumentListener documentAdapter) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void clearReadOnly(int startOffset, int length) { + // TODO: this is called from notifier loop inappropriately + // throw new NotImplementedException("intentionally not implemented"); + } + + public int computeIndexInCategory(String category, int offset) throws BadLocationException, BadPositionCategoryException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int computeNumberOfLines(String text) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public boolean containsPosition(String category, int offset, int length) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public boolean containsPositionCategory(String category) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public boolean containsReadOnly(int startOffset, int length) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void fireNewDocument(Object requester) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String get() { + String result = null; + result = data.get(0, data.getLength()); + return result; + } + + public String get(int offset, int length) throws BadLocationException { + String result = null; + try { + result = data.get(offset, length); + } catch (StringIndexOutOfBoundsException e) { + throw new BadLocationException("offset: " + offset + " length: " + length + "\ndocument length: " + data.getLength()); + } + return result; + } + + public Object getAdapter(Class adapter) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public char getChar(int offset) throws BadLocationException { + return data.get(offset); + } + + public String getContentType(int offset) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IDocumentPartitioner getDocumentPartitioner() { + // temp fix + return null; + // throw new NotImplementedException("intentionally not implemented"); + } + + public EncodingMemento getEncodingMemento() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IStructuredDocumentRegion getLastStructuredDocumentRegion() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String[] getLegalContentTypes() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String[] getLegalLineDelimiters() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int getLength() { + return data.getLength(); + } + + public String getPreferedLineDelimiter() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String getLineDelimiter(int line) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IRegion getLineInformation(int line) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IRegion getLineInformationOfOffset(int offset) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int getLineLength(int line) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int getLineOffset(int line) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int getLineOfOffset(int offset) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int getNumberOfLines() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int getNumberOfLines(int offset, int length) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public RegionParser getParser() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public ITypedRegion getPartition(int offset) throws BadLocationException { + Logger.log(Logger.WARNING, "An instance of MinimalDocument was asked for its partition, sometime indicating a deleted region was being accessed."); //$NON-NLS-1$ + return new TypedRegion(0,0, "undefined"); //$NON-NLS-1$ + //throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String[] getPositionCategories() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public Position[] getPositions(String category) throws BadPositionCategoryException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IPositionUpdater[] getPositionUpdaters() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IStructuredDocumentRegion getRegionAtCharacterOffset(int offset) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IStructuredDocumentRegionList getRegionList() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IStructuredTextReParser getReParser() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String getText() { + return data.get(0, data.getLength()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.text.IStructuredDocument#getUndoManager() + */ + public IStructuredTextUndoManager getUndoManager() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void insertPositionUpdater(IPositionUpdater updater, int index) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void makeReadOnly(int startOffset, int length) { + // TODO: this is called from notifier loop inappropriately + // throw new NotImplementedException("intentionally not implemented"); + } + + public IStructuredDocument newInstance() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#registerPostNotificationReplace(org.eclipse.jface.text.IDocumentListener, + * org.eclipse.jface.text.IDocumentExtension.IReplace) + */ + public void registerPostNotificationReplace(IDocumentListener owner, IReplace replace) throws UnsupportedOperationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removeDocumentAboutToChangeListener(IModelAboutToBeChangedListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removeDocumentChangedListener(IStructuredDocumentListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removeDocumentChangingListener(IStructuredDocumentListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removeDocumentListener(IDocumentListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removePosition(Position position) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removePosition(String category, Position position) throws BadPositionCategoryException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removePositionCategory(String category) throws BadPositionCategoryException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removePositionUpdater(IPositionUpdater updater) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void removePrenotifiedDocumentListener(IDocumentListener documentAdapter) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void replace(int offset, int length, String text) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + // data.replace(offset, length, text); + } + + public StructuredDocumentEvent replaceText(Object source, int oldStart, int replacementLength, String requestedChange) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.text.IStructuredDocument#replaceText(java.lang.Object, + * int, int, java.lang.String, boolean) + */ + public StructuredDocumentEvent replaceText(Object source, int oldStart, int replacementLength, String requestedChange, boolean ignoreReadOnlySetting) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#resumePostNotificationProcessing() + */ + public void resumePostNotificationProcessing() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public int search(int startOffset, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void set(String text) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + // data.set(text); + } + + public void setDocumentPartitioner(IDocumentPartitioner partitioner) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void setEncodingMemento(EncodingMemento encodingMemento) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public void setPreferredLineDelimiter(String delimiter) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public StructuredDocumentEvent setText(Object requester, String allText) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.text.IStructuredDocument#setUndoManager(org.eclipse.wst.sse.core.undo.StructuredTextUndoManager) + */ + public void setUndoManager(IStructuredTextUndoManager undoManager) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#startSequentialRewrite(boolean) + */ + public void startSequentialRewrite(boolean normalize) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#stopPostNotificationProcessing() + */ + public void stopPostNotificationProcessing() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentExtension#stopSequentialRewrite() + */ + public void stopSequentialRewrite() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public String getLineDelimiter() { + return null; + } + + public String getPreferredLineDelimiter() { + return null; + } + + public void setLineDelimiter(String delimiter) { + + } + + public IStructuredDocumentRegion[] getStructuredDocumentRegions() { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } + + public IStructuredDocumentRegion[] getStructuredDocumentRegions(int start, int length) { + throw new NotImplementedException("intentionally not implemented"); //$NON-NLS-1$ + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/ReadOnlyPosition.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/ReadOnlyPosition.java new file mode 100644 index 0000000..4c4bccf --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/ReadOnlyPosition.java
@@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2006 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.wst.sse.core.internal.text; + +import org.eclipse.jface.text.Position; + +class ReadOnlyPosition extends Position { + private boolean fIncludeStartOffset = false; + + public ReadOnlyPosition(int newOffset, int newLength, boolean includeStart) { + super(newOffset, newLength); + fIncludeStartOffset = includeStart; + } + + public boolean overlapsWith(int newOffset, int newLength) { + boolean overlapsWith = super.overlapsWith(newOffset, newLength); + if (overlapsWith) { + /* + * BUG157526 If at the start of the read only region and length = + * 0 most likely asking to insert and want to all inserting before + * read only region + */ + if (fIncludeStartOffset && (newLength == 0) && (this.length != 0) && (newOffset == this.offset)) { + overlapsWith = false; + } + } + return overlapsWith; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java new file mode 100644 index 0000000..1cad185 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java
@@ -0,0 +1,1700 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300427 - Comparison of String Objects using == or != + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.FindReplaceDocumentAdapter; +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTagParser; +import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextReParser; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.eclipse.wst.sse.core.internal.util.Debug; +import org.eclipse.wst.sse.core.internal.util.Utilities; +import org.eclipse.wst.sse.core.utils.StringUtils; + + +/** + * This class provides a centralized place to put "reparsing" logic. This is + * the logic that reparses the text incrementally, as a user types in new + * characters, or DOM nodes are inserted or deleted. Note: it is not a thread + * safe class. + */ +public class StructuredDocumentReParser implements IStructuredTextReParser { + protected IStructuredDocumentRegion dirtyEnd = null; + protected IStructuredDocumentRegion dirtyStart = null; + final private String doubleQuote = new String(new char[]{'\"'}); + protected final CoreNodeList EMPTY_LIST = new CoreNodeList(); + protected String fChanges; + protected String fDeletedText; + protected boolean fIsEntireDocument; + + private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter = null; + protected int fLengthDifference; + protected int fLengthToReplace; + protected Object fRequester; + protected int fStart; + // note: this is the impl class of IStructuredDocument, not the interface + // FUTURE_TO_DO: I believe some of these can be made private now.? + protected BasicStructuredDocument fStructuredDocument; + + /** + * variable used in anticiapation of multithreading + */ + protected boolean isParsing; + final private String singleQuote = new String(new char[]{'\''}); + + public StructuredDocumentReParser() { + super(); + } + + public StructuredDocumentEvent _checkBlockNodeList(List blockTagList) { + StructuredDocumentEvent result = null; + if (blockTagList != null) { + for (int i = 0; i < blockTagList.size(); i++) { + org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker blockTag = (org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker) blockTagList.get(i); + String tagName = blockTag.getTagName(); + final String tagStart = "<" + tagName; //$NON-NLS-1$ + result = checkForCriticalName(tagStart); //$NON-NLS-1$ + if (result != null) + break; + result = checkForCriticalName("</" + tagName); //$NON-NLS-1$ + if (result != null) + break; + result = checkForSelfClosing(tagStart); + if (result != null) + break; + result = checkForTransitionToOpen(tagStart); + if (result != null) + break; + } + } + return result; + } + + /** + * Checks if the start region has become self-closing. e.g., <style> -> <style/> + */ + private StructuredDocumentEvent checkForSelfClosing(String tagName) { + StructuredDocumentEvent result = null; + if (dirtyStart.getText().toLowerCase().indexOf(tagName.toLowerCase()) >= 0) { // within a start-tag + final int documentLength = fStructuredDocument.getLength(); + int end = fStart + fLengthToReplace + fChanges.length() + 1; + if (end > documentLength) + end = documentLength - 1; + final String oldText = fStructuredDocument.get(fStart, 1); + final String peek = StringUtils.paste(oldText, fChanges, 0, fLengthToReplace); + if ("/>".equals(peek)) { // Reparse afterwards if the tag became self-closing + result = reparse(dirtyStart.getStart(), documentLength - 1); + } + } + return result; + } + + /** + * Checks if the start region has become self-closing. e.g., <style/> -> <style> + */ + private StructuredDocumentEvent checkForTransitionToOpen(String tagName) { + StructuredDocumentEvent result = null; + if (dirtyStart.getText().toLowerCase().indexOf(tagName.toLowerCase()) >= 0) { // within a start-tag + final int documentLength = fStructuredDocument.getLength(); + int end = fStart + fLengthToReplace + fChanges.length() + 1; + if (end > documentLength) + end = documentLength - 1; + final String oldText = fStructuredDocument.get(fStart, 2); + final String peek = StringUtils.paste(oldText, fChanges, 0, fLengthToReplace); + if ("/>".equals(oldText) && ">".equals(peek)) { // Reparse afterwards if the block tag went from self-closing to open + result = reparse(dirtyStart.getStart(), documentLength - 1); + } + } + return result; + } + + /** + * Common utility for checking for critical word such as " <SCRIPT>" + */ + private StructuredDocumentEvent _checkForCriticalWord(String criticalTarget, boolean checkEnd) { + StructuredDocumentEvent result = null; + int documentLength = fStructuredDocument.getLength(); + int propLen = fLengthToReplace; + if (propLen > documentLength) + propLen = documentLength; + int startNeighborhood = fStart - criticalTarget.length(); + int adjustInsert = 0; + if (startNeighborhood < 0) { + adjustInsert = 0 - startNeighborhood; + startNeighborhood = 0; + } + int endNeighborhood = fStart + fLengthToReplace + criticalTarget.length() - 1; + if (endNeighborhood > documentLength) + endNeighborhood = documentLength - 1; + int oldlen = endNeighborhood - startNeighborhood; // + 1; + if (oldlen + startNeighborhood > documentLength) { + oldlen = documentLength - startNeighborhood; + } + String oldText = fStructuredDocument.get(startNeighborhood, oldlen); + String peek = StringUtils.paste(oldText, fChanges, criticalTarget.length() - adjustInsert, fLengthToReplace); + boolean isCriticalString = checkTagNames(oldText, criticalTarget, checkEnd); + boolean toBeCriticalString = checkTagNames(peek, criticalTarget, checkEnd); + if ((isCriticalString != toBeCriticalString) || // OR if both are + // critical and there's + // a change in the end + // tag ('>') + ((isCriticalString && toBeCriticalString) && (changeInIsEndedState(oldText, peek)))) { + // if it involves a change of a critical string (making one where + // there wasn't, or removing + // one where there was one) then reparse everthing. + result = reparse(0, documentLength - 1); + } + return result; + } + + private int _computeStartOfDifferences(CoreNodeList oldNodes, CoreNodeList newNodes) { + int startOfDifferences = -1; + int newNodesLength = newNodes.getLength(); + boolean foundDifference = false; + boolean done = false; + // we'll control our loop based on the old List length + int oldNodesLength = oldNodes.getLength(); + // be sure to check 'done' first, so startOfDifferences isn't + // icremented if done is true + done : while ((!done) && (++startOfDifferences < oldNodesLength)) { + IStructuredDocumentRegion oldNode = oldNodes.item(startOfDifferences); + // this lessThanEffectedRegion is to check to be sure the node is + // infact a candidate + // to be considered as "old". This check is important for the case + // where some + // text is replaceing text that + // appears identical, but is a different instance. For example, if + // the text + // is <P><B></B></P> and <B></B> is inserted at postion 3, + // resulting in <P><B></B><B></B></P> + // we do not want the + // first <B> to be considered old ... it is the new one, the + // second + // <B> is the old one. + if (_lessThanEffectedRegion(oldNode)) { + // be sure to check that we have new nodes to compare against. + if (startOfDifferences > newNodesLength) { + foundDifference = false; + done = true; + continue done; + } else { + // + IStructuredDocumentRegion newNode = newNodes.item(startOfDifferences); + // note: shift is 0 while at beginning of list, before the + // insertion (or deletion) point. After that, it is + // fStart+fLengthDifference + if (!(oldNode.sameAs(newNode, 0))) { + foundDifference = true; + done = true; + continue done; + } else { // if they are equal, then we will be keeping the + // old one, so + // we need to be sure its parentDocument is set back + // to + // the right instance + oldNode.setParentDocument(fStructuredDocument); + } + } + } else { + // we didn't literally find a difference, but we count it as + // such by implication + foundDifference = true; + done = true; + continue done; + } + } + // if we literally found a difference, then all is ok and we can + // return + // it. + // if we did not literally find one, then we have to decide why. + if (!foundDifference) { + if (newNodesLength == oldNodesLength) { // then lists are + // identical + // (and may be of zero + // length) + startOfDifferences = -1; + } else { + if (newNodesLength > oldNodesLength) { // then lists are + // identical except for + // newNodes added + startOfDifferences = oldNodesLength; + } else { + if (newNodesLength < oldNodesLength) { // then lists are + // identical except + // for old Nodes + // deleted + startOfDifferences = newNodesLength; + } + } + } + } + return startOfDifferences; + } + + private int _computeStartOfDifferences(IStructuredDocumentRegion oldNodeParam, ITextRegionList oldRegions, IStructuredDocumentRegion newNodeParam, ITextRegionList newRegions) { + int startOfDifferences = -1; + int newRegionsLength = newRegions.size(); + boolean foundDifference = false; + boolean done = false; + // we'll control our loop based on the old List length + int oldRegionsLength = oldRegions.size(); + // be sure to check 'done' first, so startOfDifferences isn't + // icremented if done is true + done : while ((!done) && (++startOfDifferences < oldRegionsLength)) { + ITextRegion oldRegion = oldRegions.get(startOfDifferences); + // this lessThanEffectedRegion is to check to be sure the node is + // infact a candidate + // to be considered as "old". This check is important for the case + // where some + // text is replaceing text that + // appears identical, but is a different instance. For example, if + // the text + // is <P><B></B></P> and <B></B> is inserted at postion 3, + // resulting in <P><B></B><B></B></P> + // we do not want the + // first <B> to be considered old ... it is the new one, the + // second + // <B> is the old one. + if (_lessThanEffectedRegion(oldNodeParam, oldRegion)) { + // be sure to check that we have new nodes to compare against. + if (startOfDifferences > newRegionsLength) { + foundDifference = false; + done = true; + continue done; + } else { + // + ITextRegion newRegion = newRegions.get(startOfDifferences); + // note: shift is 0 while at beginning of list, before the + // insertion (or deletion) point. After that, it is + // fStart+fLengthDifference + if (!(oldNodeParam.sameAs(oldRegion, newNodeParam, newRegion, 0))) { + foundDifference = true; + done = true; + continue done; + } else { + // if they are equal, then we will be keeping the old + // one. + // unlike the flatnode case, there is no reason to + // update + // the textstore, since its the same text store in + // either case + // (since its the same flatnode) + //oldRegion.setTextStore(fStructuredDocument.parentDocument); + } + } + } else { + // we didn't literally find a difference, but we count it as + // such by implication + foundDifference = true; + done = true; + continue done; + } + } + // if we literally found a difference, then all is ok and we can + // return + // it. + // if we did not literally find one, then we have to decide why. + if (!foundDifference) { + if (newRegionsLength == oldRegionsLength) { // then lists are + // identical (and may + // be of zero length) + startOfDifferences = -1; + } else { + if (newRegionsLength > oldRegionsLength) { // then lists are + // identical except + // for newRegions + // added + startOfDifferences = oldRegionsLength; + } else { + if (newRegionsLength < oldRegionsLength) { // then lists + // are identical + // except for + // old Nodes + // deleted + startOfDifferences = newRegionsLength; + } + } + } + } + return startOfDifferences; + } + + /** + * Part 1 of 2 steps to do a core_reparse + * + * Parses a portion of the current text in the IStructuredDocument and + * returns the raw result + */ + private IStructuredDocumentRegion _core_reparse_text(int rescanStart, int rescanEnd) { + fStructuredDocument.resetParser(rescanStart, rescanEnd); + return fStructuredDocument.getParser().getDocumentRegions(); + } + + /** + * Part 2 of 2 steps to do a core_reparse + * + * Integrates a list of StructuredDocumentRegions based on the current + * text contents of the IStructuredDocument into the IStructuredDocument + * data structure + */ + private StructuredDocumentEvent _core_reparse_update_model(IStructuredDocumentRegion newNodesHead, int rescanStart, int rescanEnd, CoreNodeList oldNodes, boolean firstTime) { + StructuredDocumentEvent result = null; + CoreNodeList newNodes = null; + // rescan + newNodes = new CoreNodeList(newNodesHead); + // adjust our newNode chain so the offset positions match + // our text store (not the simple string of text reparsed) + StructuredDocumentRegionIterator.adjustStart(newNodesHead, rescanStart); + // initialize the parentDocument variable of each instance in the new + // chain + StructuredDocumentRegionIterator.setParentDocument(newNodesHead, fStructuredDocument); + // initialize the structuredDocument variable of each instance in the + // new chain + //StructuredDocumentRegionIterator.setStructuredDocument(newNodesHead, + // fStructuredDocument); + // + if (firstTime) { + fStructuredDocument.setCachedDocumentRegion(newNodesHead); + fStructuredDocument.initializeFirstAndLastDocumentRegion(); + // note: since we are inserting nodes, for the first time, there + // is + // no adjustments + // to downstream stuff necessary. + result = new StructuredDocumentRegionsReplacedEvent(fStructuredDocument, fRequester, oldNodes, newNodes, fChanges, fStart, fLengthToReplace, fIsEntireDocument); + } else { + // note: integrates changes into model as a side effect + result = minimumEvent(oldNodes, newNodes); + } + result.setDeletedText(fDeletedText); + return result; + } + + private CoreNodeList _formMinimumList(CoreNodeList flatnodes, int startOfDifferences, int endOfDifferences) { + CoreNodeList minimalNodes = null; + // if startOfDifferces is still its initial value, then we have an + // empty document + if (startOfDifferences == -1) { + minimalNodes = EMPTY_LIST; + } else { + // if we do not have any flatnode in our flatnode list, then + // simply + // return our standard empty list + if (flatnodes.getLength() == 0) { + minimalNodes = EMPTY_LIST; + } else { + // if startOfDifferences is greater than endOfDifferences, + // then + // that means the calculations "crossed" each other, and + // hence, + // there really is no differences, so, again, return the empty + // list + if (startOfDifferences > endOfDifferences) { + minimalNodes = EMPTY_LIST; + } else { + // the last check be sure we have some differnces + if ((endOfDifferences > -1)) { + minimalNodes = new CoreNodeList(flatnodes.item(startOfDifferences), flatnodes.item(endOfDifferences)); + } else { + // there were no differences, the list wasn't + // minimized, so simply return it. + minimalNodes = flatnodes; + } + } + } + } + return minimalNodes; + } + + private boolean _greaterThanEffectedRegion(IStructuredDocumentRegion oldNode) { + boolean result = false; + int nodeStart = oldNode.getStartOffset(); + int changedRegionEnd = fStart + fLengthToReplace - 1; + result = nodeStart > changedRegionEnd; + return result; + } + + private boolean _greaterThanEffectedRegion(IStructuredDocumentRegion oldNode, ITextRegion oldRegion) { + boolean result = false; + int regionStartOffset = oldNode.getStartOffset(oldRegion); + int effectedRegionEnd = fStart + fLengthToReplace - 1; + result = regionStartOffset > effectedRegionEnd; + return result; + } + + private boolean _lessThanEffectedRegion(IStructuredDocumentRegion oldNode) { + boolean result = false; + int nodeEnd = oldNode.getEndOffset() - 1; + result = nodeEnd < fStart; + return result; + } + + private boolean _lessThanEffectedRegion(IStructuredDocumentRegion oldNode, ITextRegion oldRegion) { + boolean result = false; + int nodeEnd = oldNode.getEndOffset(oldRegion) - 1; + result = nodeEnd < fStart; + return result; + } + + private boolean _regionsSameKind(ITextRegion newRegion, ITextRegion oldRegion) { + boolean result = false; + // if one region is a container region, and the other not, always + // return false + // else, just check their type. + // DW druing refactoring, looks like a "typo" here, using 'old' in + // both. + // if (isContainerRegion(oldRegion) != isContainerRegion(oldRegion)) + if (isCollectionRegion(oldRegion) != isCollectionRegion(newRegion)) + result = false; + else if (oldRegion.getType().equals(newRegion.getType())) + result = true; + return result; + } + + // private boolean hasCollectionRegions(ITextRegion aRegion) { + // boolean result = false; + // if (aRegion instanceof ITextRegionCollection) { + // ITextRegionCollection regionContainter = (ITextRegionCollection) + // aRegion; + // ITextRegionList regions = regionContainter.getRegions(); + // Iterator iterator = regions.iterator(); + // while (iterator.hasNext()) { + // if (aRegion instanceof ITextRegionCollection) { + // result = true; + // break; + // } + // } + // } + // return result; + // } + /** + * This method is specifically to detect changes in 'isEnded' state, + * although it still does so with heuristics. If number of '>' changes, + * assume the isEnded state has changed. + */ + private boolean changeInIsEndedState(String oldText, String newText) { + int nOld = StringUtils.occurrencesOf(oldText, '>'); + int nNew = StringUtils.occurrencesOf(newText, '>'); + return !(nOld == nNew); + } + + private void checkAndAssignParent(IStructuredDocumentRegion oldNode, ITextRegion region) { + if (region instanceof ITextRegionContainer) { + ((ITextRegionContainer) region).setParent(oldNode); + return; + } + if (region instanceof ITextRegionCollection) { + ITextRegionCollection textRegionCollection = (ITextRegionCollection) region; + ITextRegionList regionList = textRegionCollection.getRegions(); + for (int i = 0; i < regionList.size(); i++) { + ITextRegion innerRegion = regionList.get(i); + checkAndAssignParent(oldNode, innerRegion); + } + } + } + + /** + * A change to a CDATA tag can result in all being reparsed. + */ + private StructuredDocumentEvent checkForCDATA() { + StructuredDocumentEvent result = null; + result = checkForCriticalKey("<![CDATA["); //$NON-NLS-1$ + if (result == null) + result = checkForCriticalKey("]]>"); //$NON-NLS-1$ + return result; + } + + /** + * If a comment start or end tag is being added or deleted, we'll rescan + * the whole document. The reason is that content that is revealed or + * commented out can effect the interpretation of the rest of the + * document. Note: for now this is very XML specific, can refactor/improve + * later. + */ + protected StructuredDocumentEvent checkForComments() { + StructuredDocumentEvent result = null; + result = checkForCriticalKey("<!--"); //$NON-NLS-1$ + if (result == null) + result = checkForCriticalKey("-->"); //$NON-NLS-1$ + // we'll also check for these degenerate cases + if (result == null) + result = checkForCriticalKey("<!--->"); //$NON-NLS-1$ + return result; + } + + /** + * Common utility for checking for critical word such as " <SCRIPT>" + */ + protected StructuredDocumentEvent checkForCriticalKey(String criticalTarget) { + return _checkForCriticalWord(criticalTarget, false); + } + + /** + * Common utility for checking for critical word such as " <SCRIPT>" + */ + private StructuredDocumentEvent checkForCriticalName(String criticalTarget) { + return _checkForCriticalWord(criticalTarget, true); + } + + // /** + // * Currently this method is pretty specific to ?ML + // * @deprecated - not really deprecated, but plan to make + // * protected ... I'm not sure why its public or misspelled? + // */ + protected StructuredDocumentEvent checkForCrossStructuredDocumentRegionBoundryCases() { + StructuredDocumentEvent result = null; + // Case 1: See if the language's syntax requires that multiple + // StructuredDocumentRegions be rescanned + if (result == null) { + result = checkForCrossStructuredDocumentRegionSyntax(); + } + // Case 2: "block tags" whose content is left unparsed + if (result == null) { + Object parser = fStructuredDocument.getParser(); + if (parser instanceof BlockTagParser) { + List blockTags = ((BlockTagParser) parser).getBlockMarkers(); + result = _checkBlockNodeList(blockTags); + } + } + // FUTURE_TO_DO: is there a better place to do this? + // or! do we already do it some other more central place? + if (result != null) { + result.setDeletedText(fDeletedText); + } + return result; + } + + /** + * Allow a reparser to check for extra syntactic cases that require + * parsing beyond the flatNode boundary. + * + * This implementation is very XML-centric. + */ + protected StructuredDocumentEvent checkForCrossStructuredDocumentRegionSyntax() { + StructuredDocumentEvent result; + // Case 1: Quote characters are involved + result = checkForQuotes(); + if (result == null) { + // Case 2: The input forms or undoes a comment beginning or + // comment + // end + result = checkForComments(); + } + if (result == null) { + // Case 3: The input forms or undoes a processing instruction + result = checkForPI(); + } + if (result == null) { + // Case 4: The input forms or undoes a CDATA section + result = checkForCDATA(); + } + return result; + } + + /** + * Checks to see if change request exactly matches the text it would be + * replacing. (In future, this, or similar method is where to check for + * "read only" attempted change.) + */ + private StructuredDocumentEvent checkForNoChange() { + StructuredDocumentEvent result = null; + // don't check equals unless lengths match + // should be a tiny bit faster, since usually not + // of equal lengths (I'm surprised String's equals method + // doesn't do this.) + if ((fChanges != null) && (fDeletedText != null) && (fChanges.length() == fDeletedText.length()) && (fChanges.equals(fDeletedText))) { + result = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); + ((NoChangeEvent)result).reason = NoChangeEvent.NO_CONTENT_CHANGE; + } + return result; + } + + /** + * A change to a PI tag can result in all being reparsed. + */ + private StructuredDocumentEvent checkForPI() { + StructuredDocumentEvent result = null; + result = checkForCriticalKey("<?"); //$NON-NLS-1$ + if (result == null) + result = checkForCriticalKey("?>"); //$NON-NLS-1$ + return result; + } + + /* + * For simplicity, if either text to be deleted, or text to be inserted + * contains at least one quote, we'll search for previous quote in + * document, if any, and use that document region as a dirty start, and we'll use + * end of document as dirty end. We need to assume either \" or \' is an + * acceptable quote. (NOTE: this is, loosely, an XML assumption -- other + * languages would differ, but we'll "hard code" for XML for now. + * + * future_TODO: this is a really bad heuristic ... we should be looking + * for odd number of quotes within a structuredDocumentRegion (or + * something!) This causes way too much reparsing on simple cases, like + * deleting a tag with a quoted attribute! + */ + private StructuredDocumentEvent checkForQuotes() { + // routine is supported with null or empty string meaning the same + // thing: deletion + if (fChanges == null) + fChanges = ""; //$NON-NLS-1$ + // + StructuredDocumentEvent result = null; + try { + int dirtyStartPos = -1; + String proposedDeletion = fStructuredDocument.get(fStart, fLengthToReplace); + if (fStart < fStructuredDocument.getLength()) { + if ((fChanges.indexOf(singleQuote) > -1) || (proposedDeletion.indexOf(singleQuote) > -1)) { + IRegion singleQuoteRegion = getFindReplaceDocumentAdapter().find(fStart, singleQuote, false, false, false, false); + if (singleQuoteRegion != null) { + dirtyStartPos = singleQuoteRegion.getOffset(); + } + } else if ((fChanges.indexOf(doubleQuote) > -1) || (proposedDeletion.indexOf(doubleQuote) > -1)) { + IRegion doubleQuoteRegion = getFindReplaceDocumentAdapter().find(fStart, doubleQuote, false, false, false, false); + if (doubleQuoteRegion != null) { + dirtyStartPos = doubleQuoteRegion.getOffset(); + } + } + } + if (dirtyStartPos > -1) { + // then we found one, do create new structuredDocument event + // based on the previous quote to end of document + // except, we need to be positive that the previous quote is + // in a "safe start" region (e.g. if in JSP content, we need + // to + // backup till we include the whole JSP region, in order for + // it + // to be correctly re-parsed. The backing up is done in the + // reparse/find dirty start from hint + // method. + result = reparse(dirtyStartPos, fStructuredDocument.getLength() - 1); + } + } catch (BadLocationException e) { + Logger.logException(e); + } + if (result != null) { + result.setDeletedText(fDeletedText); + } + return result; + } + + private StructuredDocumentEvent checkHeuristics() { + StructuredDocumentEvent result = null; + result = checkForNoChange(); + if (result == null) { + result = checkForCrossStructuredDocumentRegionBoundryCases(); + if (result == null) { + result = quickCheck(); + } + } + return result; + } + + /** + * Takes into account "tag name" rules for comparisons; case-insensitive. + */ + private boolean checkTagNames(String compareText, String criticalTarget, boolean checkEnd) { + boolean result = false; + if ((compareText == null) || (criticalTarget == null)) + return false; + int posOfCriticalWord = compareText.toLowerCase().indexOf(criticalTarget.toLowerCase()); + result = posOfCriticalWord > -1; + if (checkEnd && result) { + // instead of returning true right away, we'll only return true + // the + // potentially matched tag is indeed a tag, for example, if + // <SCRIPT + // becomes <SCRIPTS we don't want to say the latter is a critical + // tag + int lastPos = posOfCriticalWord + criticalTarget.length(); + if (lastPos < compareText.length()) { + char lastChar = compareText.charAt(lastPos); + // Future: check formal definition of this java method, vs. + // XML + // parsing rules + result = (!Character.isLetterOrDigit(lastChar)); + } + } + return result; + } + + /** + * The core reparsing method ... after the dirty start and dirty end have + * been calculated elsewhere, and the text updated. + */ + protected StructuredDocumentEvent core_reparse(int rescanStart, int rescanEnd, CoreNodeList oldNodes, boolean firstTime) { + IStructuredDocumentRegion newNodesHead = null; + StructuredDocumentEvent result = null; + newNodesHead = _core_reparse_text(rescanStart, rescanEnd); + result = _core_reparse_update_model(newNodesHead, rescanStart, rescanEnd, oldNodes, firstTime); + return result; + } + + /** + * Resets state to "not parsing" + */ + private synchronized void endReParse() { + isParsing = false; + dirtyStart = null; + dirtyEnd = null; + fChanges = null; + fDeletedText = null; + fIsEntireDocument = false; + } + + protected IStructuredDocumentRegion findDirtyEnd(int end) { + // Caution: here's one place we have to cast + IStructuredDocumentRegion result = fStructuredDocument.getRegionAtCharacterOffset(end); + // if not well formed, get one past, if there is something there + if ((result != null) && (!result.isEnded())) { + if (result.getNext() != null) { + result = result.getNext(); + } + } + // also, get one past if exactly equal to the end (this was needed + // as a simple fix to when a whole exact region is deleted. + // there's probably a better way. + if ((result != null) && (end == result.getEnd())) { + if (result.getNext() != null) { + result = result.getNext(); + } + } + // moved to subclass for quick transition + // 12/6/2001 - Since we've changed the parser/scanner to allow a lone + // '<' without + // always interpretting it as start of a tag name, we need to be a + // little fancier, in order + // to "skip" over any plain 'ol content between the lone '<' and any + // potential meating + // regions past plain 'ol content. + // if (isLoneOpenFollowedByContent(result) && (result.getNext() != + // null)) { + // result = result.getNext(); + // } + if (result != null) + fStructuredDocument.setCachedDocumentRegion(result); + dirtyEnd = result; + return dirtyEnd; + } + + protected void findDirtyStart(int start) { + IStructuredDocumentRegion result = fStructuredDocument.getRegionAtCharacterOffset(start); + // heuristic: if the postion is exactly equal to the start, then + // go back one more, if it exists. This prevents problems with + // insertions + // of text that should be merged with the previous node instead of + // simply hung + // off of it as a separate node (ex.: XML content inserted right + // before + // an open + // bracket should become part of the previous content node) + if (result != null) { + IStructuredDocumentRegion previous = result.getPrevious(); + if ((previous != null) && ((!(previous.isEnded())) || (start == result.getStart()))) { + result = previous; + } + // If we are now at the end of a "tag dependent" content area (or + // JSP area) + // then we need to back up all the way to the beginning of that. + IStructuredDocumentRegion potential = result; + // moved to subclass to speed transition + // while (isPartOfBlockRegion(potential)) { + // potential = potential.getPrevious(); + // } + if (potential != null) { + result = potential; + fStructuredDocument.setCachedDocumentRegion(result); + } + } + dirtyStart = result; + } + + protected CoreNodeList formOldNodes(IStructuredDocumentRegion dirtyStart, IStructuredDocumentRegion dirtyEnd) { + CoreNodeList oldNodes = new CoreNodeList(dirtyStart, dirtyEnd); + // Now save the old text, that "goes with" the old nodes and regions. + // Notice we are getting it directly from the text store + String oldText = null; + int oldStart = -1; + int oldEnd = -1; + // make sure there is some text, if not, use empty string + // (if one node is not null, the other should ALWAYS be not null too, + // since it + // would at least be equal to it.) + if (dirtyStart != null) { + oldStart = dirtyStart.getStart(); + oldEnd = dirtyEnd.getEnd(); + oldText = fStructuredDocument.get(oldStart, oldEnd - oldStart); + } else { + oldStart = 0; + oldEnd = 0; + oldText = ""; //$NON-NLS-1$ + } + // create a temporary text store for this text + SubSetTextStore subTextStore = new SubSetTextStore(oldText, oldStart, oldEnd, fStructuredDocument.getLength()); + // Now update the text store of the oldNodes + StructuredDocumentRegionIterator.setParentDocument(oldNodes, new MinimalDocument(subTextStore)); + return oldNodes; + } + + /** + * @return Returns the findReplaceDocumentAdapter. + */ + public FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() { + if (fFindReplaceDocumentAdapter == null) { + fFindReplaceDocumentAdapter = new FindReplaceDocumentAdapter(fStructuredDocument); + } + return fFindReplaceDocumentAdapter; + } + + // Note: if thead safety is needed, this and all the other public methods + // of this class + // should be synchronized. + public void initialize(Object requester, int start, int lengthToReplace, String changes) { + isParsing = true; + fRequester = requester; + fStart = start; + fLengthToReplace = lengthToReplace; + fChanges = changes; + // notice this one is derived + fLengthDifference = Utilities.calculateLengthDifference(fChanges, fLengthToReplace); + fDeletedText = fStructuredDocument.get(fStart, fLengthToReplace); + int docLength = fStructuredDocument.getLength(); + fIsEntireDocument = lengthToReplace >= docLength && docLength > 0; + } + + protected void insertNodes(IStructuredDocumentRegion previousOldNode, IStructuredDocumentRegion nextOldNode, CoreNodeList newNodes) { + // + IStructuredDocumentRegion firstNew = null; + IStructuredDocumentRegion lastNew = null; + // + IStructuredDocumentRegion oldPrevious = previousOldNode; + IStructuredDocumentRegion oldNext = nextOldNode; + // + if (newNodes.getLength() > 0) { + // get pointers + firstNew = newNodes.item(0); + lastNew = newNodes.item(newNodes.getLength() - 1); + // switch surrounding StructuredDocumentRegions' references to + // lists + if (oldPrevious != null) + oldPrevious.setNext(firstNew); + if (oldNext != null) { + oldNext.setPrevious(lastNew); + } else { + // SIDE EFFECT + // if oldNext is null, that means we are replaceing the + // lastNode in the chain, + // so we need to update the structuredDocuments lastNode as + // the + // last of the new nodes. + fStructuredDocument.setLastDocumentRegion(newNodes.item(newNodes.getLength() - 1)); + } + if (firstNew != null) + firstNew.setPrevious(oldPrevious); + if (lastNew != null) + lastNew.setNext(oldNext); + } + // else nothing to insert + } + + /** + * @param oldRegion + */ + private boolean isCollectionRegion(ITextRegion aRegion) { + return (aRegion instanceof ITextRegionCollection); + } + + /** + * @return boolean + */ + public boolean isParsing() { + return isParsing; + } + + /** + * The minimization algorithm simply checks the old nodes to see if any of + * them "survived" the rescan and are unchanged. If so, the instance of + * the old node is used instead of the new node. Before the requested + * change, need to check type, offsets, and text to determine if the same. + * After the requested change, need to check type and text, but adjust the + * offsets to what ever the change was. + */ + protected StructuredDocumentEvent minimumEvent(CoreNodeList oldNodes, CoreNodeList newNodes) { + StructuredDocumentEvent event = null; + CoreNodeList minimalOldNodes = null; + CoreNodeList minimalNewNodes = null; + // To minimize nodes, we'll collect all those + // that are not equal into old and new lists + // Note: we assume that old and new nodes + // are basically contiguous -- and we force it to be so, + // by starting at the beginning to + // find first difference, and then starting at the end to find + // last difference. Everything in between we assume is different. + // + // + // + // startOfDifferences is the index into the core node list where the + // first difference + // occurs. But it may point into the old or the new list. + int startOfDifferences = _computeStartOfDifferences(oldNodes, newNodes); + int endOfDifferencesOld = -1; + int endOfDifferencesNew = -1; + // if one of the lists are shorter than where the differences start, + // then + // then some portion of the lists are identical + if ((startOfDifferences >= oldNodes.getLength()) || (startOfDifferences >= newNodes.getLength())) { + if (oldNodes.getLength() < newNodes.getLength()) { + // Then there are new regions to add + // these lengths will cause the vector of old ones to not + // have any elements, and the vector of new regions to have + // just the new ones not in common with the old ones + //startOfDifferences should equal oldNodes.getLength(), + // calculated above on _computeStartOfDifferences + minimalOldNodes = EMPTY_LIST; + endOfDifferencesNew = newNodes.getLength() - 1; + minimalNewNodes = _formMinimumList(newNodes, startOfDifferences, endOfDifferencesNew); + } else { + if (oldNodes.getLength() > newNodes.getLength()) { + // delete old + // then there are old regions to delete + // these lengths will cause the vector of old regions to + // contain the ones to delete, and the vector of new + // regions + // not have any elements + //startOfDifferences should equal newNodes.getLength(), + // calculated above on _computeStartOfDifferences + endOfDifferencesOld = oldNodes.getLength() - 1; + minimalOldNodes = _formMinimumList(oldNodes, startOfDifferences, endOfDifferencesOld); + minimalNewNodes = EMPTY_LIST; + } else + // unlikely event + event = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); + } + } else { + // We found a normal startOfDiffernces, but have not yet found the + // ends. + // We'll look for the end of differences by going backwards down + // the two lists. + // Here we need a seperate index for each array, since they may be + // (and + // probably are) of different lengths. + int indexOld = oldNodes.getLength() - 1; + int indexNew = newNodes.getLength() - 1; + // The greaterThanEffectedRegion is important to gaurd against + // incorrect counting + // when something identical is inserted to what's already there + // (see minimization test case 5) + // Note: the indexOld > startOfDifferences keeps indexOld from + // getting too small, + // so that the subsequent oldNodes.item(indexOld) is always valid. + while ((indexOld >= startOfDifferences) && (_greaterThanEffectedRegion(oldNodes.item(indexOld)))) { + if (!(oldNodes.item(indexOld).sameAs(newNodes.item(indexNew), fLengthDifference))) { + break; + } else { + // if they are equal, then we will be keeping the old one, + // so + // we need to be sure its parentDocument is set back to + // the + // right instance + oldNodes.item(indexOld).setParentDocument(fStructuredDocument); + } + indexOld--; + indexNew--; + } + endOfDifferencesOld = indexOld; + endOfDifferencesNew = indexNew; + minimalOldNodes = _formMinimumList(oldNodes, startOfDifferences, endOfDifferencesOld); + minimalNewNodes = _formMinimumList(newNodes, startOfDifferences, endOfDifferencesNew); + } ///////////////////////////////////////// + // + IStructuredDocumentRegion firstDownStreamNode = null; + event = regionCheck(minimalOldNodes, minimalNewNodes); + if (event != null) { + firstDownStreamNode = minimalOldNodes.item(0).getNext(); + if (firstDownStreamNode != null && fLengthDifference != 0) { // if + // firstDownStream + // is + // null, + // then + // we're + // at + // the + // end + // of + // the + // document + StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); + } // + } else { + event = nodesReplacedCheck(minimalOldNodes, minimalNewNodes); + // now splice the new chain of nodes to where the old chain is (or + // was) + // the firstDownStreamNode (the first of those after the new + // nodes) + // is + // remembered as a tiny optimization. + if (minimalOldNodes.getLength() == 0 && minimalNewNodes.getLength() > 0) { + // if no old nodes are being deleted, then use the + // the newNodes offset (minus one) to find the point to + // update downstream nodes, and after updating downstream + // nodes postions, insert the new ones. + int insertOffset = minimalNewNodes.item(0).getStartOffset(); + IStructuredDocumentRegion lastOldUnchangedNode = null; + if (insertOffset > 0) { + lastOldUnchangedNode = fStructuredDocument.getRegionAtCharacterOffset(insertOffset - 1); + firstDownStreamNode = lastOldUnchangedNode.getNext(); + } else { + // we're inserting at very beginning + firstDownStreamNode = fStructuredDocument.getFirstStructuredDocumentRegion(); + // SIDE EFFECT: change the firstNode pointer if we're + // inserting at beginning + fStructuredDocument.setFirstDocumentRegion(minimalNewNodes.item(0)); + } + StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); + insertNodes(lastOldUnchangedNode, firstDownStreamNode, minimalNewNodes); + // this (nodes replaced) is the only case where we need to + // update the cached Node + reSetCachedNode(minimalOldNodes, minimalNewNodes); + } else { + firstDownStreamNode = switchNodeLists(minimalOldNodes, minimalNewNodes); + // no need to adjust the length of the new nodes themselves, + // they + // are already correct, but we do need to + // adjust all "down stream" nodes with the length of the + // insertion or deletion + // --- adjustment moved to calling method. + if (firstDownStreamNode != null) { + // && event != null + StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); + } // + // this (nodes replaced) is the only case where we need to + // update the cached Node + reSetCachedNode(minimalOldNodes, minimalNewNodes); + } + } + return event; + } + + // TODO: This should be abstract. + public IStructuredTextReParser newInstance() { + return new StructuredDocumentReParser(); + } + + protected StructuredDocumentEvent nodesReplacedCheck(CoreNodeList oldNodes, CoreNodeList newNodes) { + // actually, nothing to check here, since (and assuming) we've already + // minimized the number of nodes, and ruled out mere region changes + StructuredDocumentEvent result = new StructuredDocumentRegionsReplacedEvent(fStructuredDocument, fRequester, oldNodes, newNodes, fChanges, fStart, fLengthToReplace, fIsEntireDocument); + return result; + } + + /** + * A method to allow any heuristic "quick checks" that might cover many + * many cases, before expending the time on a full reparse. + * + */ + public StructuredDocumentEvent quickCheck() { + StructuredDocumentEvent result = null; + // if the dirty start is null, then we have an empty document. + // in which case we'll return null so everything can be + // reparsed "from scratch" . If its not null, we'll give the flatnode + // a + // chance + // to handle, but only if there is one flatnode involved. + if (dirtyStart != null && dirtyStart == dirtyEnd) { + IStructuredDocumentRegion targetNode = dirtyStart; + result = dirtyStart.updateRegion(fRequester, targetNode, fChanges, fStart, fLengthToReplace); + if (result != null) { + // at this point only, we need to update the text store and + // and downstream nodes. + // FUTURE_TO_DO: can this dependency on structuredDocument + // method be eliminated? + fStructuredDocument.updateDocumentData(fStart, fLengthToReplace, fChanges); + IStructuredDocumentRegion firstDownStreamNode = targetNode.getNext(); + // then flatnode must have been the last one, so need to + // update + // any downstream ones + if (firstDownStreamNode != null) { + StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); + } + } + } + if (result != null) { + result.setDeletedText(fDeletedText); + } + return result; + } + + /** + * If only one node is involved, sees how many regions are changed. If + * only one, then its a 'regionChanged' event ... if more than one, its a + * 'regionsReplaced' event. + */ + protected StructuredDocumentEvent regionCheck(CoreNodeList oldNodes, CoreNodeList newNodes) { + if (Debug.debugStructuredDocument) + System.out.println("IStructuredDocument::regionsReplacedCheck"); //$NON-NLS-1$ + //$NON-NLS-1$ + //$NON-NLS-1$ + // the "regionsReplaced" event could only be true if and only if the + // nodelists + // are each only "1" in length. + StructuredDocumentEvent result = null; + int oldLength = oldNodes.getLength(); + int newLength = newNodes.getLength(); + if ((oldLength != 1) || (newLength != 1)) { + result = null; + } else { + IStructuredDocumentRegion oldNode = oldNodes.item(0); + IStructuredDocumentRegion newNode = newNodes.item(0); + result = regionCheck(oldNode, newNode); + } + return result; + } + + /** + * If only one node is involved, sees how many regions are changed. If + * only one, then its a 'regionChanged' event ... if more than one, its a + * 'regionsReplaced' event. + */ + protected StructuredDocumentEvent regionCheck(IStructuredDocumentRegion oldNode, IStructuredDocumentRegion newNode) { + // + StructuredDocumentEvent result = null; + ITextRegionList oldRegions = oldNode.getRegions(); + ITextRegionList newRegions = newNode.getRegions(); + ITextRegion[] oldRegionsArray = oldRegions.toArray(); + ITextRegion[] newRegionsArray = newRegions.toArray(); + // + // for the 'regionsReplaced' event, we don't care if + // the regions changed due to type, or text, + // we'll just collect all those that are not equal + // into the old and new region lists. + // Note: we, of course, assume that old and new regions + // are basically contiguous -- and we force it to be so, + // even if not literally so, by starting at beginning to + // find first difference, and then starting at end to find + // last difference. Everything in between we assume is different. + // + // going up is easy, we start at zero in each, and continue + // till regions are not the same. + int startOfDifferences = _computeStartOfDifferences(oldNode, oldRegions, newNode, newRegions); + int endOfDifferencesOld = -1; + int endOfDifferencesNew = -1; + // + // + // if one of the lists are shorter than where the differences start, + // then + // then some portion of the lists are identical + if ((startOfDifferences >= oldRegions.size()) || (startOfDifferences >= newRegions.size())) { + if (oldRegions.size() < newRegions.size()) { + // INSERT CASE + // then there are new regions to add + // these lengths will cause the vector of old ones to not + // have any elements, and the vector of new regions to have + // just the new ones. + startOfDifferences = oldRegionsArray.length; + endOfDifferencesOld = oldRegionsArray.length - 1; + endOfDifferencesNew = newRegionsArray.length - 1; + } else { + if (oldRegions.size() > newRegions.size()) { + // DELETE CASE + // then there are old regions to delete + // these lengths will cause the vector of old regions to + // contain the ones to delete, and the vector of new + // regions + // not have any elements + startOfDifferences = newRegionsArray.length; + endOfDifferencesOld = oldRegionsArray.length - 1; + endOfDifferencesNew = newRegionsArray.length - 1; + } else { + // else the lists are identical! + // unlikely event, probably error in current design, since + // we check for identity at the very beginning of + // reparsing. + result = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); + } + } + } else { + if ((startOfDifferences > -1) && (endOfDifferencesOld < 0) && (endOfDifferencesNew < 0)) { + // We found a normal startOfDiffernces, but have not yet found + // the ends. + // We'll look for the end of differences by going backwards + // down the two lists. + // Here we need a seperate index for each array, since they + // may + // be (and + // probably are) of different lengths. + int indexOld = oldRegionsArray.length - 1; + int indexNew = newRegionsArray.length - 1; + while ((indexOld >= startOfDifferences) && (_greaterThanEffectedRegion(oldNode, oldRegionsArray[indexOld]))) { + if ((!(oldNode.sameAs(oldRegionsArray[indexOld], newNode, newRegionsArray[indexNew], fLengthDifference)))) { + //endOfDifferencesOld = indexOne; + //endOfDifferencesNew = indexTwo; + break; + } + indexOld--; + indexNew--; + } + endOfDifferencesOld = indexOld; + endOfDifferencesNew = indexNew; + } + } + // + // result != null means the impossible case above occurred + if (result == null) { + // Now form the two vectors of different regions + ITextRegionList holdOldRegions = new TextRegionListImpl(); + ITextRegionList holdNewRegions = new TextRegionListImpl(); + if (startOfDifferences > -1 && endOfDifferencesOld > -1) { + for (int i = startOfDifferences; i <= endOfDifferencesOld; i++) { + holdOldRegions.add(oldRegionsArray[i]); + } + } + if (startOfDifferences > -1 && endOfDifferencesNew > -1) { + for (int i = startOfDifferences; i <= endOfDifferencesNew; i++) { + holdNewRegions.add(newRegionsArray[i]); + } + } + if (holdOldRegions.size() == 0 && holdNewRegions.size() == 0) { + // then this means the regions were identical, which means + // someone + // pasted exactly the same thing they had selected, or !!! + // someone deleted the end bracket of the tag. !!!? + result = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); + } else { + //If both holdOldRegions and holdNewRegions are of length 1, + // then its + // a "region changed" event, else a "regions replaced" event. + // so we want the new instance of region to become part of the + // old instance of old node + if ((holdOldRegions.size() == 1) && (holdNewRegions.size() == 1) && _regionsSameKind((holdNewRegions.get(0)), (holdOldRegions.get(0)))) { + ITextRegion newOldRegion = swapNewForOldRegion(oldNode, holdOldRegions.get(0), newNode, holdNewRegions.get(0)); + // -- need to update any down stream regions, within this + // 'oldNode' + updateDownStreamRegions(oldNode, newOldRegion); + result = new RegionChangedEvent(fStructuredDocument, fRequester, oldNode, newOldRegion, fChanges, fStart, fLengthToReplace); + } else { + replaceRegions(oldNode, holdOldRegions, newNode, holdNewRegions); + // -- need to update any down stream regions, within this + // 'oldNode' + // don't need with the way replaceRegions is implemented. + // It handles. + //if(holdNewRegions.size() > 0) + //updateDownStreamRegions(oldNode, (ITextRegion) + // holdNewRegions.lastElement()); + result = new RegionsReplacedEvent(fStructuredDocument, fRequester, oldNode, holdOldRegions, holdNewRegions, fChanges, fStart, fLengthToReplace); + } + } + } + return result; + } + + /** + * An entry point for reparsing. It calculates the dirty start and dirty + * end flatnodes based on the start point and length of the changes. + * + */ + public StructuredDocumentEvent reparse() { + StructuredDocumentEvent result = null; + // if we do not have a cachedNode, then the document + // must be empty, so simply use 'null' as the dirtyStart and dirtyEnd + // otherwise, find them. + if (fStructuredDocument.getCachedDocumentRegion() != null) { + findDirtyStart(fStart); + int end = fStart + fLengthToReplace; + findDirtyEnd(end); + } + if (fStructuredDocument.getCachedDocumentRegion() != null) { + result = checkHeuristics(); + } + if (result == null) { + result = reparse(dirtyStart, dirtyEnd); + } + endReParse(); + return result; + } + + /** + * An entry point for reparsing. It calculates the dirty start and dirty + * end flatnodes based on suggested positions to begin and end. This is + * needed for cases where parsing must go beyond the immediate node and + * its direct neighbors. + * + */ + protected StructuredDocumentEvent reparse(int reScanStartHint, int reScanEndHint) { + StructuredDocumentEvent result = null; + // if we do not have a cachedNode, then the document + // must be empty, so simply use 'null' as the dirtyStart and dirtyEnd + if (fStructuredDocument.getCachedDocumentRegion() != null) { + findDirtyStart(reScanStartHint); + findDirtyEnd(reScanEndHint); + } + result = reparse(dirtyStart, dirtyEnd); + isParsing = false; + // debug + //verifyStructured(result); + return result; + } + + /** + * The core reparsing method ... after the dirty start and dirty end have + * been calculated elsewhere. + */ + protected StructuredDocumentEvent reparse(IStructuredDocumentRegion dirtyStart, IStructuredDocumentRegion dirtyEnd) { + StructuredDocumentEvent result = null; + int rescanStart = -1; + int rescanEnd = -1; + boolean firstTime = false; + // + // "save" the oldNodes (that may be replaced) in a list + CoreNodeList oldNodes = formOldNodes(dirtyStart, dirtyEnd); + if (dirtyStart == null || dirtyEnd == null) { + // dirtyStart or dirty end are null, then that means we didn't + // have + // a + // cached node, which means we have an empty document, so we + // just need to rescan the changes + rescanStart = 0; + rescanEnd = fChanges.length(); + firstTime = true; + } else { + // set the start of the text to rescan + rescanStart = dirtyStart.getStart(); + // + // set the end of the text to rescan + // notice we use the same rationale as for the rescanStart, + // with the added caveat that length has to be added to it, + // to compensate for the new text which has been added or deleted. + // If changes has zero length, then "length" will be negative, + // since + // we are deleting text. Otherwise, use the difference between + // what's selected to be replaced and the length of the new text. + rescanEnd = dirtyEnd.getEnd() + fLengthDifference; + } + // now that we have the old stuff "saved" away, update the document + // with the changes. + // FUTURE_TO_DO -- don't fire "document changed" event till later + fStructuredDocument.updateDocumentData(fStart, fLengthToReplace, fChanges); + // ------------------ now the real work + result = core_reparse(rescanStart, rescanEnd, oldNodes, firstTime); + // + // event is returned to the caller, incase there is + // some opitmization they can do + return result; + } + + protected void replaceRegions(IStructuredDocumentRegion oldNode, ITextRegionList oldRegions, IStructuredDocumentRegion newNode, ITextRegionList newRegions) { + int insertPos = -1; + ITextRegionList regions = oldNode.getRegions(); + // make a fake flatnode to be new parent of oldRegions, so their text + // will be right. + //IStructuredDocumentRegion holdOldStructuredDocumentRegion = new + // BasicStructuredDocumentRegion(oldNode); + // + // need to reset the parent of the new to-be-inserted regions to be + // the + // same oldNode that is the one having its regions changed + // DW, 4/16/2003, removed since ITextRegion no longer has parent. + // ITextRegionContainer oldParent = oldNode; + // for (int i = 0; i < newRegions.size(); i++) { + // AbstractRegion region = (AbstractRegion) newRegions.elementAt(i); + // region.setParent(oldParent); + // } + // if there are no old regions, insert the new regions according to + // offset + if (oldRegions.size() == 0) { + ITextRegion firstNewRegion = newRegions.get(0); + int firstOffset = newNode.getStartOffset(firstNewRegion); + // if at beginning, insert there + if (firstOffset == 0) { + insertPos = 0; + } else { + // + ITextRegion regionAtOffset = oldNode.getRegionAtCharacterOffset(firstOffset); + if (regionAtOffset == null) + insertPos = regions.size(); + else + insertPos = regions.indexOf(regionAtOffset); + } + } else { + // else, delete old ones before inserting new ones in their place + ITextRegion firstOldRegion = oldRegions.get(0); + insertPos = regions.indexOf(firstOldRegion); + regions.removeAll(oldRegions); + } + regions.addAll(insertPos, newRegions); + // now regions vector of each node should be of equal length, + // so go through each, and make sure the old regions + // offsets matches the new regions offsets + // (we'll just assign them all, but could be slightly more effiecient) + ITextRegionList allNewRegions = newNode.getRegions(); + for (int i = 0; i < regions.size(); i++) { + ITextRegion nextOldishRegion = regions.get(i); + ITextRegion nextNewRegion = allNewRegions.get(i); + nextOldishRegion.equatePositions(nextNewRegion); + checkAndAssignParent(oldNode, nextOldishRegion); + } + oldNode.setLength(newNode.getLength()); + oldNode.setEnded(newNode.isEnded()); + oldNode.setParentDocument(newNode.getParentDocument()); + // removed concept of part of these regions, so no longer need to do. + // for (int i = 0; i < oldRegions.size(); i++) { + // ITextRegion region = (ITextRegion) oldRegions.elementAt(i); + // region.setParent(holdOldStructuredDocumentRegion); + // } + } + + private void reSetCachedNode(CoreNodeList oldNodes, CoreNodeList newNodes) { + // use the last newNode as the new cachedNode postion, unless its null + // (e.g. when nodes are deleted) in which case, assign + // it to a "safe" node so we don't lose reference to the + // structuredDocument! + if (newNodes.getLength() > 0) { + // use last new node as the cache + fStructuredDocument.setCachedDocumentRegion(newNodes.item(newNodes.getLength() - 1)); + } else { + // if cachedNode is an old node, then we're in trouble: + // we can't leave it as the cached node! and its already + // been disconnected from the model, so we can't do getNext + // or getPrevious, so we'll get one that is right before + // (or right after) the offset of the old nodes that are being + // deleted. + // + // if newNodesHead and cachedNode are both null, then + // it means we were asked to insert an empty string into + // an empty document. So we have nothing to do here + // (that is, we have no node to cache) + // similarly if there are no new nodes and no old nodes then + // nothing to do (but that should never happen ... we shouldn't + // get there if there is no event to generate). + if ((fStructuredDocument.getCachedDocumentRegion() != null) && (oldNodes.getLength() > 0)) { + // note: we can't simple use nodeAtCharacterOffset, since it + // depends on cachedNode. + if (oldNodes.includes(fStructuredDocument.getCachedDocumentRegion())) + fStructuredDocument.setCachedDocumentRegion(fStructuredDocument.getFirstStructuredDocumentRegion()); + } + if ((fStructuredDocument.getCachedDocumentRegion() == null) && (Debug.displayWarnings)) { + // this will happen now legitamately when all text is deleted + // from a document + System.out.println("Warning: StructuredDocumentReParser::reSetCachedNode: could not find a node to cache! (its ok if all text deleted)"); //$NON-NLS-1$ + } + } + } + + public void setStructuredDocument(IStructuredDocument newStructuredDocument) { + // NOTE: this method (and class) depend on being able to + // do the following cast (i.e. references some fields directly) + fStructuredDocument = (BasicStructuredDocument) newStructuredDocument; + fFindReplaceDocumentAdapter = null; + } + + private IStructuredDocumentRegion splice(CoreNodeList oldNodes, CoreNodeList newNodes) { + // + IStructuredDocumentRegion firstOld = null; + IStructuredDocumentRegion firstNew = null; + IStructuredDocumentRegion lastOld = null; + IStructuredDocumentRegion lastNew = null; + // + IStructuredDocumentRegion oldPrevious = null; + IStructuredDocumentRegion oldNext = null; + IStructuredDocumentRegion newPrevious = null; + IStructuredDocumentRegion newNext = null; + // + // if called with both arguments empty lists, we can disregard. + // this happens, for example, when some text is replaced with the + // identical text. + if ((oldNodes.getLength() == 0) && (newNodes.getLength() == 0)) { + return null; + } + // get pointers + if (newNodes.getLength() > 0) { + firstNew = newNodes.item(0); + lastNew = newNodes.item(newNodes.getLength() - 1); + } + // + if (oldNodes.getLength() > 0) { + firstOld = oldNodes.item(0); + lastOld = oldNodes.item(oldNodes.getLength() - 1); + if (firstOld != null) + oldPrevious = firstOld.getPrevious(); + if (lastOld != null) + oldNext = lastOld.getNext(); + } + // handle switch + if (newNodes.getLength() > 0) { + // switch surrounding StructuredDocumentRegions' references to + // lists + if (oldPrevious != null) + oldPrevious.setNext(firstNew); + if (newPrevious != null) + newPrevious.setNext(firstOld); + if (oldNext != null) + oldNext.setPrevious(lastNew); + if (newNext != null) + newNext.setPrevious(lastOld); + // switch list pointers to surrounding StructuredDocumentRegions + if (firstOld != null) + firstOld.setPrevious(newPrevious); + if (lastOld != null) + lastOld.setNext(newNext); + if (firstNew != null) + firstNew.setPrevious(oldPrevious); + if (lastNew != null) + lastNew.setNext(oldNext); + } else { + // short circuit when there are no new nodes + if (oldPrevious != null) + oldPrevious.setNext(oldNext); + if (oldNext != null) + oldNext.setPrevious(oldPrevious); + } + // + // SIDE EFFECTs + // if we have oldNodes, and if oldNext or oldPrevious is null, + // that means we are replacing + // the lastNode or firstNode the structuredDocuments's chain of nodes, + // so we need to update the structuredDocuments last or first Node + // as the last or first of the new nodes. + // (and sometimes even these will be null! such as when deleting all + // text in a document). + if ((oldNext == null) && (oldNodes.getLength() > 0)) { + if (newNodes.getLength() > 0) { + fStructuredDocument.setLastDocumentRegion(lastNew); + } else { + // in this case, the last node is being deleted, but not + // replaced + // with anything. In this case, we can just back up one + // from the first old node + fStructuredDocument.setLastDocumentRegion(firstOld.getPrevious()); + } + } + if ((oldPrevious == null) && (oldNodes.getLength() > 0)) { + if (newNodes.getLength() > 0) { + fStructuredDocument.setFirstDocumentRegion(firstNew); + } else { + // in this case the first node is being deleted, but not + // replaced + // with anything. So, we just go one forward past the last old + // node. + fStructuredDocument.setFirstDocumentRegion(lastOld.getNext()); + } + } + // as a tiny optimization, we return the first of the downstream + // nodes, + // if any + return oldNext; + } + + /** + * The purpose of this method is to "reuse" the old container region, when + * found to be same (so same instance doesn't change). The goal is to + * "transform" the old region, so its equivelent to the newly parsed one. + * + */ + private ITextRegion swapNewForOldRegion(IStructuredDocumentRegion oldNode, ITextRegion oldRegion, IStructuredDocumentRegion newNode, ITextRegion newRegion) { + // makes the old region instance the correct size. + oldRegion.equatePositions(newRegion); + // adjusts old node instance appropriately + oldNode.setLength(newNode.getLength()); + oldNode.setEnded(newNode.isEnded()); + // we do have to set the parent document, since the oldNode's + // were set to a temporary one, then newNode's have the + // right one. + oldNode.setParentDocument(newNode.getParentDocument()); + // if we're transforming a container region, we need to be sure to + // transfer the new embedded regions, to the old parent + // Note: if oldRegion hasEmbeddedRegions, then we know the + // newRegion does too, since we got here because they were the + // same type. + if (isCollectionRegion(oldRegion)) { // || + // hasContainerRegions(oldRegion)) + // { + transferEmbeddedRegions(oldNode, (ITextRegionContainer) oldRegion, (ITextRegionContainer) newRegion); + } + return oldRegion; + } + + private IStructuredDocumentRegion switchNodeLists(CoreNodeList oldNodes, CoreNodeList newNodes) { + IStructuredDocumentRegion result = splice(oldNodes, newNodes); + // ensure that the old nodes hold no references to the existing model + if (oldNodes.getLength() > 0) { + IStructuredDocumentRegion firstItem = oldNodes.item(0); + firstItem.setPrevious(null); + IStructuredDocumentRegion lastItem = oldNodes.item(oldNodes.getLength() - 1); + lastItem.setNext(null); + } + return result; + } + + /** + * The purpose of this method is to "reuse" the old container region, when + * found to be same (so same instance doesn't change). The goal is to + * "transform" the old region, so its equivelent to the newly parsed one. + * + */ + private void transferEmbeddedRegions(IStructuredDocumentRegion oldNode, ITextRegionContainer oldRegion, ITextRegionContainer newRegion) { + // the oldRegion should already have the right parent, since + // we got here because all's equivelent except the region + // postions have changed. + //oldRegion.setParent(newRegion.getParent()); + // but we should check if there's "nested" embedded regions, and if + // so, we can just move them over. setting their parent as this old + // region. + ITextRegionList newRegionsToTransfer = newRegion.getRegions(); + oldRegion.setRegions(newRegionsToTransfer); + Iterator newRegionsInOldOne = newRegionsToTransfer.iterator(); + while (newRegionsInOldOne.hasNext()) { + ITextRegion newOne = (ITextRegion) newRegionsInOldOne.next(); + if (isCollectionRegion(newOne)) { // || + // hasContainerRegions(newOne)) { + //((ITextRegionContainer) newOne).setParent(oldRegion); + oldRegion.setRegions(newRegion.getRegions()); + } + } + } + + private void updateDownStreamRegions(IStructuredDocumentRegion flatNode, ITextRegion lastKnownRegion) { + // so all regions after the last known region (last known to be ok) + // have to have their start and end values adjusted. + ITextRegionList regions = flatNode.getRegions(); + int listLength = regions.size(); + int startIndex = 0; + // first, loop through to find where to start + for (int i = 0; i < listLength; i++) { + ITextRegion region = regions.get(i); + if (region == lastKnownRegion) { + startIndex = i; + break; + } + } + // now, beginning one past the last known one, loop + // through to end of list, adjusting the start and end postions. + startIndex++; + for (int j = startIndex; j < listLength; j++) { + ITextRegion region = regions.get(j); + region.adjustStart(fLengthDifference); + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentRegionEnumeration.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentRegionEnumeration.java new file mode 100644 index 0000000..1191198 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentRegionEnumeration.java
@@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + + + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.util.Debug; + + +public class StructuredDocumentRegionEnumeration implements Enumeration { + + private int count; + private IStructuredDocumentRegion head; + private IStructuredDocumentRegion oldHead; + + /** + * StructuredDocumentRegionEnumeration constructor comment. + */ + public StructuredDocumentRegionEnumeration(IStructuredDocumentRegion newHead) { + super(); + IStructuredDocumentRegion countNode = head = newHead; + while (countNode != null) { + count++; + countNode = countNode.getNext(); + } + if (Debug.DEBUG > 5) { + System.out.println("N Nodes in StructuredDocumentRegionEnumeration Contructor: " + count); //$NON-NLS-1$ + } + } + + /** + * StructuredDocumentRegionEnumeration constructor comment. + */ + public StructuredDocumentRegionEnumeration(IStructuredDocumentRegion start, IStructuredDocumentRegion end) { + super(); + IStructuredDocumentRegion countNode = head = start; + if ((start == null) || (end == null)) { + // error condition + count = 0; + return; + } + //If both nodes are non-null, we assume there is always at least one + // item + count = 1; + while (countNode != end) { + count++; + countNode = countNode.getNext(); + } + if (org.eclipse.wst.sse.core.internal.util.Debug.DEBUG > 5) { + System.out.println("N Nodes in StructuredDocumentRegionEnumeration Contructor: " + count); //$NON-NLS-1$ + } + } + + /** + * hasMoreElements method comment. + */ + public synchronized boolean hasMoreElements() { + return count > 0; + } + + /** + * nextElement method comment. + */ + public synchronized Object nextElement() { + if (count > 0) { + count--; + oldHead = head; + head = head.getNext(); + return oldHead; + } + throw new NoSuchElementException("StructuredDocumentRegionEnumeration"); //$NON-NLS-1$ + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentRegionIterator.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentRegionIterator.java new file mode 100644 index 0000000..c121b31 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentRegionIterator.java
@@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + + + +import java.util.Vector; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.util.Assert; + + +public class StructuredDocumentRegionIterator { + + public final static IStructuredDocumentRegion adjustStart(IStructuredDocumentRegion headNode, int adjustment) { + IStructuredDocumentRegion aNode = headNode; + while (aNode != null) { + aNode.adjustStart(adjustment); + aNode = aNode.getNext(); + } + return headNode; + } + + public final static int countRegions(IStructuredDocumentRegionList flatNodes) { + int result = 0; + if (flatNodes != null) { + int length = flatNodes.getLength(); + for (int i = 0; i < length; i++) { + IStructuredDocumentRegion node = flatNodes.item(i); + // don't know why, but we're getting null pointer exceptions + // in this method + if (node != null) { + result = result + node.getNumberOfRegions(); + } + } + } + return result; + } + + public final static String getText(CoreNodeList flatNodes) { + String result = null; + if (flatNodes == null) { + result = ""; //$NON-NLS-1$ + } else { + StringBuffer buff = new StringBuffer(); + //IStructuredDocumentRegion aNode = null; + int length = flatNodes.getLength(); + for (int i = 0; i < length; i++) { + buff.append(flatNodes.item(i).getText()); + } + result = buff.toString(); + } + return result; + } + + public final static CoreNodeList setParentDocument(CoreNodeList nodelist, IStructuredDocument textStore) { + Assert.isNotNull(nodelist, "nodelist was null in CoreNodeList::setTextStore(CoreNodeList, StructuredTextStore)"); //$NON-NLS-1$ + int len = nodelist.getLength(); + for (int i = 0; i < len; i++) { + IStructuredDocumentRegion node = nodelist.item(i); + //Assert.isNotNull(node, "who's putting null in the node list? in + // CoreNodeList::setTextStore(CoreNodeList, + // StructuredTextStore)"); //$NON-NLS-1$ + node.setParentDocument(textStore); + } + return nodelist; + } + + // public final static IStructuredDocumentRegion + // setStructuredDocument(IStructuredDocumentRegion headNode, + // BasicStructuredDocument structuredDocument) { + // IStructuredDocumentRegion aNode = headNode; + // while (aNode != null) { + // aNode.setParentDocument(structuredDocument); + // aNode = (IStructuredDocumentRegion) aNode.getNext(); + // } + // return headNode; + // } + public final static IStructuredDocumentRegion setParentDocument(IStructuredDocumentRegion headNode, IStructuredDocument document) { + IStructuredDocumentRegion aNode = headNode; + while (aNode != null) { + aNode.setParentDocument(document); + aNode = aNode.getNext(); + } + return headNode; + } + + public final static Vector toVector(IStructuredDocumentRegion headNode) { + IStructuredDocumentRegion aNode = headNode; + Vector v = new Vector(); + while (aNode != null) { + v.addElement(aNode); + aNode = aNode.getNext(); + } + return v; + } + + /** + * + */ + private StructuredDocumentRegionIterator() { + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentSequentialRewriteTextStore.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentSequentialRewriteTextStore.java new file mode 100644 index 0000000..5c31aa8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentSequentialRewriteTextStore.java
@@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import org.eclipse.jface.text.ITextStore; +import org.eclipse.jface.text.SequentialRewriteTextStore; + +public class StructuredDocumentSequentialRewriteTextStore extends SequentialRewriteTextStore implements CharSequence, IRegionComparible { + + /** + * @param source + */ + public StructuredDocumentSequentialRewriteTextStore(ITextStore source) { + super(source); + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#charAt(int) + */ + public char charAt(int index) { + return get(index); + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#length() + */ + public int length() { + return getLength(); + } + + /** + * @param c + * @param d + * @return + */ + private boolean matchesIgnoreCase(char c1, char c2) { + // we check both case conversions to handle those few cases, + // in languages such as Turkish, which have some characters + // which sort of have 3 cases. + boolean result = false; + if (Character.toUpperCase(c1) == Character.toUpperCase(c2)) + result = true; + else if (Character.toLowerCase(c1) == Character.toLowerCase(c2)) + result = true; + return result; + } + + public boolean regionMatches(int offset, int length, String stringToCompare) { + boolean result = false; + int compareLength = stringToCompare.length(); + if (compareLength == length) { + int endOffset = offset + length; + if (endOffset <= length()) { + result = regionMatches(offset, stringToCompare); + } + } + + return result; + } + + /** + * This method assumes all lengths have been checked and fall withint + * exceptable limits + * + * @param offset + * @param stringToCompare + * @return + */ + private boolean regionMatches(int offset, String stringToCompare) { + boolean result = true; + int stringOffset = 0; + int len = stringToCompare.length(); + for (int i = offset; i < len; i++) { + if (charAt(i) != stringToCompare.charAt(stringOffset++)) { + result = false; + break; + } + } + return result; + } + + public boolean regionMatchesIgnoreCase(int offset, int length, String stringToCompare) { + boolean result = false; + int compareLength = stringToCompare.length(); + if (compareLength == length) { + int endOffset = offset + length; + if (endOffset <= length()) { + result = regionMatchesIgnoreCase(offset, stringToCompare); + } + } + + return result; + } + + private boolean regionMatchesIgnoreCase(int offset, String stringToCompare) { + boolean result = true; + int stringOffset = 0; + int len = stringToCompare.length(); + for (int i = offset; i < len; i++) { + if (!matchesIgnoreCase(charAt(i), stringToCompare.charAt(stringOffset++))) { + result = false; + break; + } + } + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#subSequence(int, int) + */ + public CharSequence subSequence(int start, int end) { + + return get(start, end - start); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentTextStore.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentTextStore.java new file mode 100644 index 0000000..e825e72 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentTextStore.java
@@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Viacheslav Kabanovich/Exadel 97817 Wrong algoritm in class StructuredDocumentTextStore + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=97817 + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import org.eclipse.jface.text.GapTextStore; +import org.eclipse.jface.text.ITextStore; + +public class StructuredDocumentTextStore implements ITextStore, CharSequence, IRegionComparible { + + private GapTextStore fInternalStore; + + /** + * + */ + public StructuredDocumentTextStore() { + this(50, 300); + } + + /** + * @param lowWatermark + * @param highWatermark + */ + public StructuredDocumentTextStore(int lowWatermark, int highWatermark) { + super(); + fInternalStore = new GapTextStore(lowWatermark, highWatermark); + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#charAt(int) + */ + public char charAt(int index) { + return fInternalStore.get(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.ITextStore#get(int) + */ + public char get(int offset) { + + return fInternalStore.get(offset); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.ITextStore#get(int, int) + */ + public String get(int offset, int length) { + + return fInternalStore.get(offset, length); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.ITextStore#getLength() + */ + public int getLength() { + + return fInternalStore.getLength(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#length() + */ + public int length() { + + return fInternalStore.getLength(); + } + + private boolean matchesIgnoreCase(char c1, char c2) { + // we check both case conversions to handle those few cases, + // in languages such as Turkish, which have some characters + // which sort of have 3 cases. + boolean result = false; + if (Character.toUpperCase(c1) == Character.toUpperCase(c2)) + result = true; + else if (Character.toLowerCase(c1) == Character.toLowerCase(c2)) + result = true; + return result; + } + + public boolean regionMatches(int offset, int length, String stringToCompare) { + boolean result = false; + int compareLength = stringToCompare.length(); + if (compareLength == length) { + int endOffset = offset + length; + if (endOffset <= length()) { + result = regionMatches(offset, stringToCompare); + } + } + + return result; + } + + /** + * This method assumes all lengths have been checked and fall withint + * exceptable limits + * + * @param offset + * @param stringToCompare + * @return + */ + private boolean regionMatches(int offset, String stringToCompare) { + boolean result = true; + int stringOffset = 0; + int end = offset + stringToCompare.length(); + for (int i = offset; i < end; i++) { + if (charAt(i) != stringToCompare.charAt(stringOffset++)) { + result = false; + break; + } + } + return result; + } + + public boolean regionMatchesIgnoreCase(int offset, int length, String stringToCompare) { + boolean result = false; + int compareLength = stringToCompare.length(); + if (compareLength == length) { + int endOffset = offset + length; + if (endOffset <= length()) { + result = regionMatchesIgnoreCase(offset, stringToCompare); + } + } + + return result; + } + + private boolean regionMatchesIgnoreCase(int offset, String stringToCompare) { + boolean result = true; + int stringOffset = 0; + int end = offset + stringToCompare.length(); + for (int i = offset; i < end; i++) { + if (!matchesIgnoreCase(charAt(i), stringToCompare.charAt(stringOffset++))) { + result = false; + break; + } + } + return result; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.ITextStore#replace(int, int, + * java.lang.String) + */ + public void replace(int offset, int length, String text) { + fInternalStore.replace(offset, length, text); + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.ITextStore#set(java.lang.String) + */ + public void set(String text) { + fInternalStore.set(text); + + } + + /* + * (non-Javadoc) + * + * @see java.lang.CharSequence#subSequence(int, int) + */ + public CharSequence subSequence(int start, int end) { + // convert 'end' to 'length' + return fInternalStore.get(start, end - start); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/SubSetTextStore.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/SubSetTextStore.java new file mode 100644 index 0000000..caca8b9 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/SubSetTextStore.java
@@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +/** + * This is a convience or utility class that allows you to make a copy of a + * part of a larger text store, but have the copy behave as if it was the + * larger text store. + * + * In other words, it provides a subset of a larger document, that "looks like" + * the orginal document. That is, "looks like" in terms of offsets and lengths. + * Valid access can only be made to data between the orignal offsets, even + * though those offsets are in the same units at the original, and even though + * the length appears to be long. + * + * For example, if a subsettext store is created for the def part of abcdefgh, + * then get(3,5) is valid, getLength is 8. Any other access, such as + * getChar(2), would be invalid. + */ +import org.eclipse.jface.text.ITextStore; + +/** + * Similar to basics of IDocument, but the offsets are mapped from coordinates + * of underlying storage to a "virtual" document. + */ +public class SubSetTextStore implements ITextStore { + private int pseudoBeginOffset; // maps to "zero" postion of new text + //private int pseudoEndOffset; + private int pseudoLength; // length of old/original document + private StringBuffer stringBuffer = new StringBuffer(); + + /** + * SubSetTextStore constructor comment. + * + * @param initialContent + * java.lang.String + */ + public SubSetTextStore(String initialContent, int beginOffset, int endOffset, int originalDocumentLength) { + super(); + pseudoBeginOffset = beginOffset; + //pseudoEndOffset = endOffset; + // used to be originalDocument.getLength ... not sure if used, or + // which + // is right + pseudoLength = originalDocumentLength; + stringBuffer = new StringBuffer(initialContent); + //set(initialContent); + } + + // this is our "private" get, which methods in this class should + // use to get using "real" coordinates of underlying representation. + private String _get(int begin, int length) { + char[] chars = new char[length]; + int srcEnd = begin + length; + stringBuffer.getChars(begin, srcEnd, chars, 0); + return new String(chars); + } + + public char get(int offset) { + return stringBuffer.charAt(offset - pseudoBeginOffset); + } + + /** + * @return java.lang.String + * @param begin + * int + * @param end + * int + */ + public String get(int begin, int length) { + // remap the begin and end to "appear" to be in the + // same coordinates of the original parentDocument + return _get(begin - pseudoBeginOffset, length); + } + + /** + * @return java.lang.String + * @param begin + * int + * @param end + * int + */ + public char getChar(int pos) { + // remap the begin and end to "appear" to be in the + // same coordinates of the original parentDocument + return get(pos - pseudoBeginOffset); + } + + /** + * We redefine getLength so its not the true length of this sub-set + * document, but the length of the original. This is needed, as a simple + * example, if you want to see if the pseudo end is equal the last + * position of the original document. + */ + public int getLength() { + return pseudoLength; + } + + /** + * Returns the length as if considered a true, standalone document + */ + public int getTrueLength() { + return stringBuffer.length(); + } + + public void replace(int begin, int length, String changes) { + // remap the begin and end to "appear" to be in the + // same coordinates of the original parentDocument + int end = begin + length; + stringBuffer.replace(begin - pseudoBeginOffset, end, changes); + } + + public void set(String text) { + stringBuffer.setLength(0); + stringBuffer.append(text); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/TextRegionListImpl.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/TextRegionListImpl.java new file mode 100644 index 0000000..9f62b78 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/TextRegionListImpl.java
@@ -0,0 +1,236 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300434 - Make inner classes static where possible + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + + +public class TextRegionListImpl implements ITextRegionList { + + static private class NullIterator implements Iterator { + public NullIterator() { + } + + public boolean hasNext() { + return false; + } + + public Object next() { + throw new NoSuchElementException(); + } + + public void remove() { + throw new UnsupportedOperationException("can not remove regions via iterator"); //$NON-NLS-1$ + + } + + } + + private static class RegionIterator implements Iterator { + private ITextRegion[] fIteratorRegions; + private int index = -1; + private int maxindex = -1; + + public RegionIterator(ITextRegion[] regions) { + fIteratorRegions = regions; + maxindex = fIteratorRegions.length - 1; + } + + public boolean hasNext() { + return index < maxindex; + } + + public Object next() { + if (!(index < maxindex)) + throw new NoSuchElementException(); + return fIteratorRegions[++index]; + } + + public void remove() { + if (index < 0) { + // next() has never been called + throw new IllegalStateException("can not remove regions without prior invocation of next()"); //$NON-NLS-1$ + } + throw new UnsupportedOperationException("can not remove regions via iterator"); //$NON-NLS-1$ + } + + } + + private final static int growthConstant = 2; + + private ITextRegion[] fRegions; + private int fRegionsCount = 0; + + public TextRegionListImpl() { + super(); + } + + public TextRegionListImpl(ITextRegionList regionList) { + this(); + fRegions = (ITextRegion[]) regionList.toArray().clone(); + fRegionsCount = fRegions.length; + } + + public boolean add(ITextRegion region) { + if (region == null) + return false; + ensureCapacity(fRegionsCount + 1); + fRegions[fRegionsCount++] = region; + return true; + } + + public boolean addAll(int insertPos, ITextRegionList newRegionList) { + // beginning of list is 0 to insertPos-1 + // remainder of list is insertPos to fRegionsCount + // resulting total will be be fRegionsCount + newRegions.size() + if (insertPos < 0 || insertPos > fRegionsCount) { + throw new ArrayIndexOutOfBoundsException(insertPos); + } + + int newRegionListSize = newRegionList.size(); + + ensureCapacity(fRegionsCount + newRegionListSize); + + int numMoved = fRegionsCount - insertPos; + if (numMoved > 0) + System.arraycopy(fRegions, insertPos, fRegions, insertPos + newRegionListSize, numMoved); + + if (newRegionList instanceof TextRegionListImpl && ((TextRegionListImpl) newRegionList).fRegions != null) { + System.arraycopy(((TextRegionListImpl) newRegionList).fRegions, 0, fRegions, insertPos, newRegionListSize); + } + else { + for (int i = 0; i < newRegionListSize; i++) { + fRegions[insertPos++] = newRegionList.get(i); + } + } + fRegionsCount += newRegionListSize; + return newRegionListSize != 0; + } + + public void clear() { + // note: size of array is not reduced! + fRegionsCount = 0; + } + + private void ensureCapacity(int needed) { + if (fRegions == null) { + // first time + fRegions = new ITextRegion[needed]; + return; + } + int oldLength = fRegions.length; + if (oldLength < needed) { + ITextRegion[] oldAdapters = fRegions; + ITextRegion[] newAdapters = new ITextRegion[needed + growthConstant]; + System.arraycopy(oldAdapters, 0, newAdapters, 0, fRegionsCount); + fRegions = newAdapters; + } + } + + public ITextRegion get(int index) { + // fRegionCount may not equal fRegions.length + if (index < 0 || index >= fRegionsCount) { + throw new ArrayIndexOutOfBoundsException(index); + } + return fRegions[index]; + } + + public int indexOf(ITextRegion region) { + int result = -1; + if (region != null) { + if (fRegions != null) { + for (int i = 0; i < fRegions.length; i++) { + if (region.equals(fRegions[i])) { + result = i; + break; + } + } + } + } + return result; + } + + public boolean isEmpty() { + return fRegionsCount == 0; + } + + public Iterator iterator() { + if (size() == 0) { + return new NullIterator(); + } else { + return new RegionIterator(toArray()); + } + } + + public ITextRegion remove(int index) { + // much more efficient ways to implement this, but + // I doubt if called often + ITextRegion oneToRemove = get(index); + remove(oneToRemove); + return oneToRemove; + } + + public void remove(ITextRegion a) { + if (fRegions == null || a == null) + return; + int newIndex = 0; + ITextRegion[] newRegions = new ITextRegion[fRegionsCount]; + int oldRegionCount = fRegionsCount; + boolean found = false; + for (int oldIndex = 0; oldIndex < oldRegionCount; oldIndex++) { + ITextRegion candidate = fRegions[oldIndex]; + if (a == candidate) { + fRegionsCount--; + found = true; + } else + newRegions[newIndex++] = fRegions[oldIndex]; + } + if (found) + fRegions = newRegions; + } + + public void removeAll(ITextRegionList regionList) { + // much more efficient ways to implement this, but + // I doubt if called often + if (regionList != null) { + for (int i = 0; i < regionList.size(); i++) { + this.remove(regionList.get(i)); + } + } + + } + + public int size() { + return fRegionsCount; + } + + public ITextRegion[] toArray() { + // return "clone" of internal array + ITextRegion[] newArray = new ITextRegion[fRegionsCount]; + System.arraycopy(fRegions, 0, newArray, 0, fRegionsCount); + return newArray; + } + + public void trimToSize() { + if (fRegions.length > fRegionsCount) { + ITextRegion[] newRegions = new ITextRegion[fRegionsCount]; + System.arraycopy(fRegions, 0, newRegions, 0, fRegionsCount); + fRegions = newRegions; + } + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/IStructuredRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/IStructuredRegion.java new file mode 100644 index 0000000..f12b76a --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/IStructuredRegion.java
@@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text.rules; + +import org.eclipse.jface.text.IRegion; + +/** + * Like super class except allows length and offset to be modified. This is + * convenient for some algorithms, and allows region objects to be reused. + * Note: There MIGHT be some code that assumes regions are immutable. This + * class would not be appropriate for those uses. + */ +public interface IStructuredRegion extends IRegion { + void setLength(int length); + + void setOffset(int offset); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/IStructuredTypedRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/IStructuredTypedRegion.java new file mode 100644 index 0000000..1ea21a9 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/IStructuredTypedRegion.java
@@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text.rules; + +import org.eclipse.jface.text.ITypedRegion; + +/** + * Similar to extended interface, except it allows the length, offset, and + * type to be set. This is useful when iterating through a number of "small" + * regions, that all map to the the same partion regions. + */ +public interface IStructuredTypedRegion extends IStructuredRegion, ITypedRegion { + void setType(String partitionType); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/SimpleStructuredRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/SimpleStructuredRegion.java new file mode 100644 index 0000000..4ca1ded --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/SimpleStructuredRegion.java
@@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text.rules; + +import org.eclipse.jface.text.IRegion; + + + +/** + * Similar to jface region except we wanted a setting on length + */ +public class SimpleStructuredRegion implements IStructuredRegion { + /** The region length */ + private int fLength; + + /** The region offset */ + private int fOffset; + + /** + * Create a new region. + * + * @param offset + * the offset of the region + * @param length + * the length of the region + */ + public SimpleStructuredRegion(int offset, int length) { + fOffset = offset; + fLength = length; + } + + /** + * Two regions are equal if they have the same offset and length. + * + * @see Object#equals + */ + public boolean equals(Object o) { + if (o instanceof IRegion) { + IRegion r = (IRegion) o; + return r.getOffset() == fOffset && r.getLength() == fLength; + } + return false; + } + + /* + * @see IRegion#getLength + */ + public int getLength() { + return fLength; + } + + /* + * @see IRegion#getOffset + */ + public int getOffset() { + return fOffset; + } + + /** + * @see Object#hashCode hascode is overridden since we provide our own + * equals. + */ + public int hashCode() { + return (fOffset << 24) | (fLength << 16); + } + + /** + * Sets the length. + * + * @param length + * The length to set + */ + public void setLength(int length) { + fLength = length; + } + + public void setOffset(int offset) { + fOffset = offset; + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/SimpleStructuredTypedRegion.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/SimpleStructuredTypedRegion.java new file mode 100644 index 0000000..1822a8a --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/SimpleStructuredTypedRegion.java
@@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text.rules; + + + + +/** + * Similar jace TypedRegion, but had to subclass our version which allowed + * length to be set. + */ +public class SimpleStructuredTypedRegion extends SimpleStructuredRegion implements IStructuredTypedRegion { + + /** The region's type */ + private String fType; + + /** + * Creates a typed region based on the given specification. + * + * @param offset + * the region's offset + * @param length + * the region's length + * @param type + * the region's type + */ + public SimpleStructuredTypedRegion(int offset, int length, String type) { + super(offset, length); + fType = type; + } + + /** + * Two typed positions are equal if they have the same offset, length, and + * type. + * + * @see Object#equals + */ + public boolean equals(Object o) { + if (o instanceof SimpleStructuredTypedRegion) { + SimpleStructuredTypedRegion r = (SimpleStructuredTypedRegion) o; + return super.equals(r) && ((fType == null && r.getType() == null) || fType.equals(r.getType())); + } + return false; + } + + /* + * @see ITypedRegion#getType() + */ + public String getType() { + return fType; + } + + /* + * @see Object#hashCode + */ + public int hashCode() { + int type = fType == null ? 0 : fType.hashCode(); + return super.hashCode() | type; + } + + public void setType(String type) { + fType = type; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer s = new StringBuffer(); + s.append(getOffset()); + s.append(":"); //$NON-NLS-1$ + s.append(getLength()); + s.append(" - "); //$NON-NLS-1$ + s.append(getType()); + return s.toString(); + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/StructuredTextPartitioner.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/StructuredTextPartitioner.java new file mode 100644 index 0000000..445e5f2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/rules/StructuredTextPartitioner.java
@@ -0,0 +1,655 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.text.rules; + + + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.wst.sse.core.internal.ltk.parser.IBlockedStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.parser.ForeignRegion; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextPartitioner; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.eclipse.wst.sse.core.text.IStructuredPartitions; + + +/** + * Base Document partitioner for StructuredDocuments. BLOCK_TEXT ITextRegions + * have a partition type of BLOCK or BLOCK:TAGNAME if a surrounding tagname + * was recorded. + * + * Subclasses should synchronize access to <code>internalReusedTempInstance</code> using the lock + * <code>PARTITION_LOCK</code>. + */ +public class StructuredTextPartitioner implements IDocumentPartitioner, IStructuredTextPartitioner { + + static class CachedComputedPartitions { + int fLength; + int fOffset; + ITypedRegion[] fPartitions; + boolean isInValid; + + CachedComputedPartitions(int offset, int length, ITypedRegion[] partitions) { + fOffset = offset; + fLength = length; + fPartitions = partitions; + isInValid = true; + } + } + + private CachedComputedPartitions cachedPartitions = new CachedComputedPartitions(-1, -1, null); + protected String[] fSupportedTypes = null; + protected IStructuredTypedRegion internalReusedTempInstance = new SimpleStructuredTypedRegion(0, 0, IStructuredPartitions.DEFAULT_PARTITION); + protected IStructuredDocument fStructuredDocument; + + protected final Object PARTITION_LOCK = new Object(); + + /** + * StructuredTextPartitioner constructor comment. + */ + public StructuredTextPartitioner() { + super(); + } + + /** + * Returns the partitioning of the given range of the connected document. + * There must be a document connected to this partitioner. + * + * Note: this shouldn't be called directly by clients, unless they control + * the threading that includes modifications to the document. Otherwise + * the document could be modified while partitions are being computed. We + * advise that clients use the computePartitions API directly from the + * document, so they won't have to worry about that. + * + * @param offset + * the offset of the range of interest + * @param length + * the length of the range of interest + * @return the partitioning of the range + */ + public ITypedRegion[] computePartitioning(int offset, int length) { + if (fStructuredDocument == null) { + throw new IllegalStateException("document partitioner is not connected"); //$NON-NLS-1$ + } + ITypedRegion[] results = null; + + synchronized (cachedPartitions) { + if ((!cachedPartitions.isInValid) && (offset == cachedPartitions.fOffset) && (length == cachedPartitions.fLength)) + results = cachedPartitions.fPartitions; + } + + if (results == null) { + if (length == 0) { + results = new ITypedRegion[]{getPartition(offset)}; + } else { + List list = new ArrayList(); + int endPos = offset + length; + if (endPos > fStructuredDocument.getLength()) { + // This can occur if the model instance is being + // changed + // and everyone's not yet up to date + return new ITypedRegion[]{createPartition(offset, length, getUnknown())}; + } + int currentPos = offset; + IStructuredTypedRegion previousPartition = null; + while (currentPos < endPos) { + IStructuredTypedRegion partition = null; + synchronized (PARTITION_LOCK) { + internalGetPartition(currentPos, false); + currentPos += internalReusedTempInstance.getLength(); + + // check if this partition just continues last one + // (type is the same), + // if so, just extend length of last one, not need to + // create new + // instance. + if (previousPartition != null && internalReusedTempInstance.getType().equals(previousPartition.getType())) { + // same partition type + previousPartition.setLength(previousPartition.getLength() + internalReusedTempInstance.getLength()); + } + else { + // not the same, so add to list + partition = createNewPartitionInstance(); + } + } + if (partition != null) { + list.add(partition); + // and make current, previous + previousPartition = partition; + } + } + results = new ITypedRegion[list.size()]; + list.toArray(results); + } + if (results.length > 0) { + // truncate returned results to requested range + if (results[0].getOffset() < offset && results[0] instanceof IStructuredRegion) { + ((IStructuredRegion) results[0]).setOffset(offset); + } + int lastEnd = results[results.length - 1].getOffset() + results[results.length - 1].getLength(); + if (lastEnd > offset + length && results[results.length - 1] instanceof IStructuredRegion) { + ((IStructuredRegion) results[results.length - 1]).setLength(offset + length - results[results.length - 1].getOffset()); + } + } + synchronized (cachedPartitions) { + cachedPartitions.fLength = length; + cachedPartitions.fOffset = offset; + cachedPartitions.fPartitions = results; + cachedPartitions.isInValid = false; + } + } + return results; + } + + private void invalidatePartitionCache() { + synchronized (cachedPartitions) { + cachedPartitions.isInValid = true; + } + } + + /** + * Connects the document to the partitioner, i.e. indicates the begin of + * the usage of the receiver as partitioner of the given document. + */ + public synchronized void connect(IDocument document) { + if (document instanceof IStructuredDocument) { + invalidatePartitionCache(); + this.fStructuredDocument = (IStructuredDocument) document; + } else { + throw new IllegalArgumentException("This class and API are for Structured Documents only"); //$NON-NLS-1$ + } + } + + /** + * Determines if the given ITextRegionContainer itself contains another + * ITextRegionContainer + * + * @param ITextRegionContainer + * @return boolean + */ + protected boolean containsEmbeddedRegion(IStructuredDocumentRegion container) { + boolean containsEmbeddedRegion = false; + + ITextRegionList regions = container.getRegions(); + for (int i = 0; i < regions.size(); i++) { + ITextRegion region = regions.get(i); + if (region instanceof ITextRegionContainer) { + containsEmbeddedRegion = true; + break; + } + } + return containsEmbeddedRegion; + } + + private IStructuredTypedRegion createNewPartitionInstance() { + synchronized (PARTITION_LOCK) { + return new SimpleStructuredTypedRegion(internalReusedTempInstance.getOffset(), internalReusedTempInstance.getLength(), internalReusedTempInstance.getType()); + } + } + + /** + * Creates the concrete partition from the given values. Returns a new + * instance for each call. + * + * Subclasses may override. + * + * @param offset + * @param length + * @param type + * @return ITypedRegion + * + * TODO: should be protected + */ + public IStructuredTypedRegion createPartition(int offset, int length, String type) { + return new SimpleStructuredTypedRegion(offset, length, type); + } + + /** + * Disconnects the document from the partitioner, i.e. indicates the end + * of the usage of the receiver as partitioner of the given document. + * + * @see org.eclipse.jface.text.IDocumentPartitioner#disconnect() + */ + public synchronized void disconnect() { + invalidatePartitionCache(); + this.fStructuredDocument = null; + } + + /** + * Informs about a forthcoming document change. + * + * @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + invalidatePartitionCache(); + } + + /** + * The document has been changed. The partitioner updates the set of + * regions and returns whether the structure of the document partitioning + * has been changed, i.e. whether partitions have been added or removed. + * + * @see org.eclipse.jface.text.IDocumentPartitioner#documentChanged(DocumentEvent) + */ + public boolean documentChanged(DocumentEvent event) { + boolean result = false; + if (event instanceof StructuredDocumentRegionsReplacedEvent) { + // partitions don't always change while document regions do, + // but that's the only "quick check" we have. + // I'm not sure if something more sophisticated will be needed + // in the future. (dmw, 02/18/04). + result = true; + } + return result; + } + + protected boolean doParserSpecificCheck(int offset, boolean partitionFound, IStructuredDocumentRegion sdRegion, IStructuredDocumentRegion previousStructuredDocumentRegion, ITextRegion next, ITextRegion previousStart) { + // this (conceptually) abstract method is not concerned with + // specific region types + return false; + } + + protected IStructuredDocumentRegion getParserSpecificPreviousRegion(IStructuredDocumentRegion currentRegion) { + return currentRegion != null ? currentRegion.getPrevious() : null; + } + + /** + * Returns the content type of the partition containing the given + * character position of the given document. The document has previously + * been connected to the partitioner. + * + * @see org.eclipse.jface.text.IDocumentPartitioner#getContentType(int) + */ + public String getContentType(int offset) { + return getPartition(offset).getType(); + } + + /** + * To be used by default! + */ + public String getDefaultPartitionType() { + + return IStructuredPartitions.DEFAULT_PARTITION; + } + + /** + * Returns the set of all possible content types the partitioner supports. + * I.e. Any result delivered by this partitioner may not contain a content + * type which would not be included in this method's result. + * + * @see org.eclipse.jface.text.IDocumentPartitioner#getLegalContentTypes() + */ + public java.lang.String[] getLegalContentTypes() { + if (fSupportedTypes == null) { + initLegalContentTypes(); + } + return fSupportedTypes; + } + + /** + * Returns the partition containing the given character position of the + * given document. The document has previously been connected to the + * partitioner. + * + * Note: this shouldn't be called directly by clients, unless they control + * the threading that includes modifications to the document. Otherwise + * the document could be modified while partitions are being computed. We + * advise that clients use the getPartition API directly from the + * document, so they won't have to worry about that. + * + * + * + * @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int) + */ + public ITypedRegion getPartition(int offset) { + internalGetPartition(offset, true); + return createNewPartitionInstance(); + } + + protected String getPartitionFromBlockedText(ITextRegion region, int offset, String result) { + // parser sensitive code was moved to subclass for quick transition + // this (conceptually) abstract version isn't concerned with blocked + // text + + return result; + } + + protected String getPartitionType(ForeignRegion region, int offset) { + String tagname = region.getSurroundingTag(); + String result = null; + if (tagname != null) { + result = "BLOCK:" + tagname.toUpperCase(Locale.ENGLISH); //$NON-NLS-1$ + } else { + result = "BLOCK"; //$NON-NLS-1$ + } + return result; + } + + + protected String getPartitionType(IBlockedStructuredDocumentRegion blockedStructuredDocumentRegion, int offset) { + String result = null; + ITextRegionList regions = blockedStructuredDocumentRegion.getRegions(); + + // regions should never be null, or hold zero regions, but just in + // case... + if (regions != null && regions.size() > 0) { + if (regions.size() == 1) { + // if only one, then its a "pure" blocked note. + // if more than one, then must contain some embedded region + // container + ITextRegion blockedRegion = regions.get(0); + // double check for code safefy, though should always be true + if (blockedRegion instanceof ForeignRegion) { + result = getPartitionType((ForeignRegion) blockedRegion, offset); + } + } else { + // must have some embedded region container, so we'll make + // sure we'll get the appropriate one + result = getReleventRegionType(blockedStructuredDocumentRegion, offset); + } + } + return result; + } + + /** + * Method getPartitionType. + * + * @param region + * @return String + */ + private String getPartitionType(ITextRegion region) { + // if it get's to this "raw" level, then + // must be default. + return getDefaultPartitionType(); + } + + /** + * Returns the partition based on region type. This basically maps from + * one region-type space to another, higher level, region-type space. + * + * @param region + * @param offset + * @return String + */ + public String getPartitionType(ITextRegion region, int offset) { + String result = getDefaultPartitionType(); + // if (region instanceof ContextRegionContainer) { + // result = getPartitionType((ITextRegionContainer) region, offset); + // } else { + if (region instanceof ITextRegionContainer) { + result = getPartitionType((ITextRegionContainer) region, offset); + } + + result = getPartitionFromBlockedText(region, offset, result); + + return result; + + } + + /** + * Similar to method with 'ITextRegion' as argument, except for + * RegionContainers, if it has embedded regions, then we need to drill + * down and return DocumentPartition based on "lowest level" region type. + * For example, in <body id=" <%= object.getID() %>" > The text between + * <%= and %> would be a "java region" not an "HTML region". + */ + protected String getPartitionType(ITextRegionContainer region, int offset) { + // TODO this method needs to be 'cleaned up' after refactoring + // its instanceof logic seems messed up now. + String result = null; + if (region != null) { + ITextRegion coreRegion = region; + if (coreRegion instanceof ITextRegionContainer) { + result = getPartitionType((ITextRegionContainer) coreRegion, ((ITextRegionContainer) coreRegion).getRegions(), offset); + } else { + result = getPartitionType(region); + } + } else { + result = getPartitionType((ITextRegion) region, offset); + } + + return result; + } + + private String getPartitionType(ITextRegionContainer coreRegion, ITextRegionList regions, int offset) { + String result = null; + for (int i = 0; i < regions.size(); i++) { + ITextRegion region = regions.get(i); + if (coreRegion.containsOffset(region, offset)) { + result = getPartitionType(region, offset); + break; + } + } + return result; + } + + /** + * Computes the partition type for the zero-length partition between a + * start tag and end tag with the given name regions. + * + * @param previousStartTagNameRegion + * @param nextEndTagNameRegion + * @return String + */ + public String getPartitionTypeBetween(IStructuredDocumentRegion previousNode, IStructuredDocumentRegion nextNode) { + return getDefaultPartitionType(); + } + + /** + * Return the ITextRegion at the given offset. For most cases, this will + * be the flatNode itself. Should it contain an embedded + * ITextRegionContainer, will return the internal region at the offset + * + * + * @param flatNode + * @param offset + * @return ITextRegion + */ + private String getReleventRegionType(IStructuredDocumentRegion flatNode, int offset) { + // * Note: the original form of this method -- which returned "deep" + // region, isn't that + // * useful, after doing parent elimination refactoring, + // * since once the deep region is returned, its hard to get its text + // or offset without + // * proper parent. + ITextRegion resultRegion = null; + if (containsEmbeddedRegion(flatNode)) { + resultRegion = flatNode.getRegionAtCharacterOffset(offset); + if (resultRegion instanceof ITextRegionContainer) { + resultRegion = flatNode.getRegionAtCharacterOffset(offset); + ITextRegionList regions = ((ITextRegionContainer) resultRegion).getRegions(); + for (int i = 0; i < regions.size(); i++) { + ITextRegion region = regions.get(i); + if (flatNode.getStartOffset(region) <= offset && offset < flatNode.getEndOffset(region)) { + resultRegion = region; + break; + } + } + } + } else { + resultRegion = flatNode; + } + return resultRegion.getType(); + } + + /** + * To be used, instead of default, when there is some thing surprising + * about are attempt to partition + */ + protected String getUnknown() { + return IStructuredPartitions.UNKNOWN_PARTITION; + } + + /** + * to be abstract eventually + */ + protected void initLegalContentTypes() { + fSupportedTypes = new String[]{IStructuredPartitions.DEFAULT_PARTITION, IStructuredPartitions.UNKNOWN_PARTITION}; + } + + /** + * Returns the partition containing the given character position of the + * given document. The document has previously been connected to the + * partitioner. If the checkBetween parameter is true, an offset between a + * start and end tag will return a zero-length region. + */ + private void internalGetPartition(int offset, boolean checkBetween) { + if (fStructuredDocument == null) { + throw new IllegalStateException("document partitioner is not connected"); //$NON-NLS-1$ + } + + boolean partitionFound = false; + int docLength = fStructuredDocument.getLength(); + // get document region type and map to partition type : + // Note: a partion can be smaller than a flatnode, if that flatnode + // contains a region container. + // That's why we need to get "relevent region". + IStructuredDocumentRegion structuredDocumentRegion = fStructuredDocument.getRegionAtCharacterOffset(offset); + // flatNode is null if empty document + // this is king of a "normal case" for empty document + if (structuredDocumentRegion == null) { + if (docLength == 0) { + /* + * In order to prevent infinite error loops, this partition + * must never have a zero length unless the document is also + * zero length + */ + setInternalPartition(offset, 0, getDefaultPartitionType()); + partitionFound = true; + } + else { + /* + * This case is "unusual". When would region be null, and + * document longer than 0. I think this means something's "out + * of sync". And we may want to "flag" that fact and just + * return one big region of 'unknown', instead of one + * character at a time. + */ + setInternalPartition(offset, 1, getUnknown()); + partitionFound = true; + } + } + else if (checkBetween) { + // dmw: minimizes out to the first if test above + // if (structuredDocumentRegion == null && docLength == 0) { + // // known special case for an empty document + // setInternalPartition(offset, 0, getDefault()); + // partitionFound = true; + // } + // else + if (structuredDocumentRegion.getStartOffset() == offset) { + IStructuredDocumentRegion previousStructuredDocumentRegion = getParserSpecificPreviousRegion(structuredDocumentRegion); + if (previousStructuredDocumentRegion != null) { + ITextRegion next = structuredDocumentRegion.getRegionAtCharacterOffset(offset); + ITextRegion previousStart = previousStructuredDocumentRegion.getRegionAtCharacterOffset(previousStructuredDocumentRegion.getStartOffset()); + partitionFound = doParserSpecificCheck(offset, partitionFound, structuredDocumentRegion, previousStructuredDocumentRegion, next, previousStart); + } + } + } + + if (!partitionFound && structuredDocumentRegion != null) { + /* We want the actual ITextRegion and not a possible ITextRegionCollection that + * could be returned by IStructuredDocumentRegion#getRegionAtCharacterOffset + * This allows for correct syntax highlighting and content assist. + */ + DeepRegion resultRegion = getDeepRegionAtCharacterOffset(structuredDocumentRegion, offset); + partitionFound = isDocumentRegionBasedPartition(structuredDocumentRegion, resultRegion.region, offset); + if (!partitionFound) { + if (resultRegion.region != null) { + String type = getPartitionType(resultRegion.region, offset); + setInternalPartition(offset, resultRegion.end - offset, type); + } else { + // can happen at EOF + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=224886 + // The unknown type was causing problems with content assist in JSP documents + setInternalPartition(offset, 1, getDefaultPartitionType()); + } + } + } + } + + private static class DeepRegion { + int end; + ITextRegion region; + DeepRegion(ITextRegion r, int e) { + region = r; + end = e; + } + } + + /** + * <p>Unlike {@link IStructuredDocumentRegion#getRegionAtCharacterOffset(int)} this will dig + * into <code>ITextRegionCollection</code> to find the region containing the given offset</p> + * + * @param region the containing region of the given <code>offset</code> + * @param offset to the overall offset in the document. + * @return the <code>ITextRegion</code> containing the given <code>offset</code>, will never be + * a <code>ITextRegionCollextion</code> + */ + private DeepRegion getDeepRegionAtCharacterOffset(IStructuredDocumentRegion region, int offset) { + ITextRegion text = region.getRegionAtCharacterOffset(offset); + int end = region.getStartOffset(); + if (text != null) + end += text.getStart(); + while (text instanceof ITextRegionCollection) { + text = ((ITextRegionCollection) text).getRegionAtCharacterOffset(offset); + end += text.getStart(); + } + if (text != null) + end += text.getLength(); + return new DeepRegion(text, end); + } + + /** + * Provides for a per-StructuredDocumentRegion override selecting the + * partition type using more than just a single ITextRegion. + * + * @param structuredDocumentRegion + * the StructuredDocumentRegion + * @param containedChildRegion + * an ITextRegion within the given StructuredDocumentRegion + * that would normally determine the partition type by itself + * @param offset + * the document offset + * @return true if the partition type will be overridden, false to + * continue normal processing + */ + protected boolean isDocumentRegionBasedPartition(IStructuredDocumentRegion structuredDocumentRegion, ITextRegion containedChildRegion, int offset) { + return false; + } + + public IDocumentPartitioner newInstance() { + return new StructuredTextPartitioner(); + } + + protected void setInternalPartition(int offset, int length, String type) { + synchronized (PARTITION_LOCK) { + internalReusedTempInstance.setOffset(offset); + internalReusedTempInstance.setLength(length); + internalReusedTempInstance.setType(type); + } + } + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/CommandCursorPosition.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/CommandCursorPosition.java new file mode 100644 index 0000000..439cf52 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/CommandCursorPosition.java
@@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + + + +public interface CommandCursorPosition { + + /** + * Returns the cursor position to be set to after this command is redone. + * + * @return int + */ + int getRedoCursorPosition(); + + /** + * Returns the length of text to be selected after this command is redone. + * + * @return int + */ + int getRedoSelectionLength(); + + /** + * Returns the cursor position to be set to after this command is undone. + * + * @return int + */ + int getUndoCursorPosition(); + + /** + * Returns the length of text to be selected after this command is undone. + * + * @return int + */ + int getUndoSelectionLength(); + + /** + * Sets the cursor position to be used after this command is redone. + */ + void setRedoCursorPosition(int cursorPosition); + + /** + * Sets the length of text to be selected after this command is redone. + */ + void setRedoSelectionLength(int selectionLength); + + /** + * Sets the cursor position to be used after this command is undone. + */ + void setUndoCursorPosition(int cursorPosition); + + /** + * Sets the length of text to be selected after this command is undone. + */ + void setUndoSelectionLength(int selectionLength); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/IDocumentSelectionMediator.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/IDocumentSelectionMediator.java new file mode 100644 index 0000000..5632884 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/IDocumentSelectionMediator.java
@@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + +import org.eclipse.jface.text.IDocument; + + +public interface IDocumentSelectionMediator { + /** + * Returns the document selection mediator's input document. + * + * @return the document selection mediator's input document + */ + IDocument getDocument(); + + /** + * Sets a new selection in the document as a result of an undo operation. + * + * UndoDocumentEvent contains the requester of the undo operation, and the + * offset and length of the new selection. Implementation of + * IDocumentSelectionMediator can check if it's the requester that caused + * the new selection, and decide if the new selection should be applied. + */ + void undoOperationSelectionChanged(UndoDocumentEvent event); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/IStructuredTextUndoManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/IStructuredTextUndoManager.java new file mode 100644 index 0000000..80351ba --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/IStructuredTextUndoManager.java
@@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CommandStack; + +public interface IStructuredTextUndoManager { + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, int cursorPosition, int selectionLength); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label, int cursorPosition, int selectionLength); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label, String description); + + /** + * Begin recording undo transactions. + */ + void beginRecording(Object requester, String label, String description, int cursorPosition, int selectionLength); + + /** + * Connect the mediator to the undo manager. + */ + void connect(IDocumentSelectionMediator mediator); + + /** + * Disable undo management. + */ + void disableUndoManagement(); + + /** + * Disconnect the mediator from the undo manager. + */ + void disconnect(IDocumentSelectionMediator mediator); + + /** + * Enable undo management. + */ + void enableUndoManagement(); + + /** + * End recording undo transactions. + */ + void endRecording(Object requester); + + /** + * End recording undo transactions. + */ + void endRecording(Object requester, int cursorPosition, int selectionLength); + + /** + * <p> + * Normally, the undo manager can figure out the best times when to end a + * pending command and begin a new one ... to the structure of a structued + * document. There are times, however, when clients may wish to override + * those algorithms and end one earlier than normal. The one known case is + * for multipage editors. If a user is on one page, and type '123' as + * attribute value, then click around to other parts of page, or different + * pages, then return to '123|' and type 456, then "undo" they typically + * expect the undo to just undo what they just typed, the 456, not the + * whole attribute value. + * <p> + * If there is no pending command, the request is ignored. + */ + public void forceEndOfPendingCommand(Object requester, int currentPosition, int length); + + /** + * Some clients need to do complicated things with undo stack. Plus, in + * some cases, if clients setCommandStack temporarily, they have + * reponsibility to set back to original one when finished. + */ + public CommandStack getCommandStack(); + + /** + * Get the redo command even if it's not committed yet. + */ + Command getRedoCommand(); + + /** + * Get the undo command even if it's not committed yet. + */ + Command getUndoCommand(); + + /** + * Redo the last command in the undo manager. + */ + void redo(); + + /** + * Redo the last command in the undo manager and notify the requester + * about the new selection. + */ + void redo(IDocumentSelectionMediator requester); + + /** + * Returns whether at least one text change can be repeated. A text change + * can be repeated only if it was executed and rolled back. + * + * @return <code>true</code> if at least on text change can be repeated + */ + boolean redoable(); + + /** + * Set the command stack. + */ + void setCommandStack(CommandStack commandStack); + + /** + * Undo the last command in the undo manager. + */ + void undo(); + + /** + * Undo the last command in the undo manager and notify the requester + * about the new selection. + */ + void undo(IDocumentSelectionMediator requester); + + /** + * Returns whether at least one text change can be rolled back. + * + * @return <code>true</code> if at least one text change can be rolled + * back + */ + boolean undoable(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCommand.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCommand.java new file mode 100644 index 0000000..b085f5b --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCommand.java
@@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + + + +public interface StructuredTextCommand { + + String getTextDeleted(); + + int getTextEnd(); + + String getTextInserted(); + + int getTextStart(); + + void setTextDeleted(String textDeleted); + + void setTextEnd(int textEnd); + + void setTextInserted(String textInserted); + + void setTextStart(int textStart); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCommandImpl.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCommandImpl.java new file mode 100644 index 0000000..70f2bc2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCommandImpl.java
@@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + + + +import org.eclipse.emf.common.command.AbstractCommand; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + + +public class StructuredTextCommandImpl extends AbstractCommand implements StructuredTextCommand { + + protected IDocument fDocument = null; // needed for updating the text + protected String fTextDeleted = null; + protected int fTextEnd = -1; + protected String fTextInserted = null; + protected int fTextStart = -1; + + /** + * We have no-arg constructor non-public to force document to be specfied. + * + */ + protected StructuredTextCommandImpl() { + super(); + } + + public StructuredTextCommandImpl(IDocument document) { + this(); + fDocument = document; // needed for updating the text + } + + public void execute() { + } + + /** + * getTextDeleted method comment. + */ + public java.lang.String getTextDeleted() { + return fTextDeleted; + } + + /** + * textEnd is the same as (textStart + textInserted.length()) + */ + public int getTextEnd() { + return fTextEnd; + } + + /** + * getTextInserted method comment. + */ + public java.lang.String getTextInserted() { + return fTextInserted; + } + + /** + * getTextStart method comment. + */ + public int getTextStart() { + return fTextStart; + } + + protected boolean prepare() { + return true; + } + + public void redo() { + if (fDocument instanceof IStructuredDocument) { + // note: one of the few places we programatically ignore read-only + // settings + ((IStructuredDocument) fDocument).replaceText(this, fTextStart, fTextDeleted.length(), fTextInserted, true); + } else { + try { + fDocument.replace(fTextStart, fTextDeleted.length(), fTextInserted); + } catch (BadLocationException e) { + // assumed impossible, for now + Logger.logException(e); + } + } + } + + /** + * setTextDeleted method comment. + */ + public void setTextDeleted(java.lang.String textDeleted) { + fTextDeleted = textDeleted; + } + + /** + * setTextEnd method comment. + */ + public void setTextEnd(int textEnd) { + fTextEnd = textEnd; + } + + /** + * setTextInserted method comment. + */ + public void setTextInserted(java.lang.String textInserted) { + fTextInserted = textInserted; + } + + /** + * setTextStart method comment. + */ + public void setTextStart(int textStart) { + fTextStart = textStart; + } + + public void undo() { + if (fDocument instanceof IStructuredDocument) { + // note: one of the few places we programatically ignore read-only + // settings + ((IStructuredDocument) fDocument).replaceText(this, fTextStart, fTextInserted.length(), fTextDeleted, true); + } else { + try { + fDocument.replace(fTextStart, fTextInserted.length(), fTextDeleted); + } catch (BadLocationException e) { + // assumed impossible, for now + Logger.logException(e); + } + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCompoundCommandImpl.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCompoundCommandImpl.java new file mode 100644 index 0000000..ddf7636 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextCompoundCommandImpl.java
@@ -0,0 +1,260 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + + + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CompoundCommand; + + + +public class StructuredTextCompoundCommandImpl extends CompoundCommand implements CommandCursorPosition { + protected int fRedoCursorPosition = -1; + protected int fRedoSelectionLength = 0; + + protected int fUndoCursorPosition = -1; + protected int fUndoSelectionLength = 0; + + /** + * StructuredTextCompoundCommandImpl constructor comment. + */ + public StructuredTextCompoundCommandImpl() { + super(); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param resultIndex + * int + */ + public StructuredTextCompoundCommandImpl(int resultIndex) { + super(resultIndex); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param resultIndex + * int + * @param commandList + * java.util.List + */ + public StructuredTextCompoundCommandImpl(int resultIndex, java.util.List commandList) { + super(resultIndex, commandList); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param resultIndex + * int + * @param label + * java.lang.String + */ + public StructuredTextCompoundCommandImpl(int resultIndex, String label) { + super(resultIndex, label); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param resultIndex + * int + * @param label + * java.lang.String + * @param commandList + * java.util.List + */ + public StructuredTextCompoundCommandImpl(int resultIndex, String label, java.util.List commandList) { + super(resultIndex, label, commandList); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param resultIndex + * int + * @param label + * java.lang.String + * @param description + * java.lang.String + */ + public StructuredTextCompoundCommandImpl(int resultIndex, String label, String description) { + super(resultIndex, label, description); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param resultIndex + * int + * @param label + * java.lang.String + * @param description + * java.lang.String + * @param commandList + * java.util.List + */ + public StructuredTextCompoundCommandImpl(int resultIndex, String label, String description, java.util.List commandList) { + super(resultIndex, label, description, commandList); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param commandList + * java.util.List + */ + public StructuredTextCompoundCommandImpl(java.util.List commandList) { + super(commandList); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param label + * java.lang.String + */ + public StructuredTextCompoundCommandImpl(String label) { + super(label); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param label + * java.lang.String + * @param commandList + * java.util.List + */ + public StructuredTextCompoundCommandImpl(String label, java.util.List commandList) { + super(label, commandList); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param label + * java.lang.String + * @param description + * java.lang.String + */ + public StructuredTextCompoundCommandImpl(String label, String description) { + super(label, description); + } + + /** + * StructuredTextCompoundCommandImpl constructor comment. + * + * @param label + * java.lang.String + * @param description + * java.lang.String + * @param commandList + * java.util.List + */ + public StructuredTextCompoundCommandImpl(String label, String description, java.util.List commandList) { + super(label, description, commandList); + } + + /** + * Returns the cursor position to be set to after this command is redone. + * + * @return int + */ + public int getRedoCursorPosition() { + int cursorPosition = -1; + + if (fRedoCursorPosition != -1) + cursorPosition = fRedoCursorPosition; + else if (!commandList.isEmpty()) { + int commandListSize = commandList.size(); + Command lastCommand = (Command) commandList.get(commandListSize - 1); + + if (lastCommand instanceof CommandCursorPosition) + cursorPosition = ((CommandCursorPosition) lastCommand).getRedoCursorPosition(); + } + + return cursorPosition; + } + + /** + * Returns the length of text to be selected after this command is redone. + * + * @return int + */ + public int getRedoSelectionLength() { + return fRedoSelectionLength; + } + + /** + * Returns the cursor position to be set to after this command is undone. + * + * @return int + */ + public int getUndoCursorPosition() { + int cursorPosition = -1; + + if (fUndoCursorPosition != -1) + cursorPosition = fUndoCursorPosition; + else if (!commandList.isEmpty()) { + // never used + //int commandListSize = commandList.size(); + Command firstCommand = (Command) commandList.get(0); + + if (firstCommand instanceof CommandCursorPosition) + cursorPosition = ((CommandCursorPosition) firstCommand).getUndoCursorPosition(); + } + + return cursorPosition; + } + + /** + * Returns the length of text to be selected after this command is undone. + * + * @return int + */ + public int getUndoSelectionLength() { + return fUndoSelectionLength; + } + + /** + * Sets the cursor position to be used after this command is redone. + */ + public void setRedoCursorPosition(int cursorPosition) { + fRedoCursorPosition = cursorPosition; + } + + /** + * Sets the length of text to be selected after this command is redone. + */ + public void setRedoSelectionLength(int selectionLength) { + fRedoSelectionLength = selectionLength; + } + + /** + * Sets the cursor position to be used after this command is undone. + */ + public void setUndoCursorPosition(int cursorPosition) { + fUndoCursorPosition = cursorPosition; + } + + /** + * Sets the length of text to be selected after this command is undone. + */ + public void setUndoSelectionLength(int selectionLength) { + fUndoSelectionLength = selectionLength; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextUndoManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextUndoManager.java new file mode 100644 index 0000000..93a9b91 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/StructuredTextUndoManager.java
@@ -0,0 +1,650 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Jesper Steen Møller - initial IDocumentExtension4 support - #102822 + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + +import java.util.EventObject; + +import org.eclipse.emf.common.command.BasicCommandStack; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CommandStack; +import org.eclipse.emf.common.command.CommandStackListener; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension4; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.internal.provisional.events.NewDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.util.Assert; +import org.eclipse.wst.sse.core.internal.util.Utilities; + +public class StructuredTextUndoManager implements IStructuredTextUndoManager { + + class InternalCommandStackListener implements CommandStackListener { + public void commandStackChanged(EventObject event) { + resetInternalCommands(); + } + } + + class InternalStructuredDocumentListener implements IStructuredDocumentListener { + + public void newModel(NewDocumentEvent structuredDocumentEvent) { + // Do nothing. Do not push the new model's structuredDocument + // changes + // onto the undo command stack, or else the user may be able to + // undo + // an existing file to an empty file. + } + + public void noChange(NoChangeEvent structuredDocumentEvent) { + // Since "no change", do nothing. + } + + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent) { + processStructuredDocumentEvent(structuredDocumentEvent); + } + + private void processStructuredDocumentEvent(String textDeleted, String textInserted, int textStart, int textEnd) { + if (fTextCommand != null && textStart == fTextCommand.getTextEnd()) { + // append to the text command + fTextCommand.setTextDeleted(fTextCommand.getTextDeleted().concat(textDeleted)); + fTextCommand.setTextInserted(fTextCommand.getTextInserted().concat(textInserted)); + fTextCommand.setTextEnd(textEnd); + } + else if (fTextCommand != null && textStart == fTextCommand.getTextStart() - 1 && textEnd <= fTextCommand.getTextEnd() - 1 && textDeleted.length() == 1 && textInserted.length() == 0 && fTextCommand.getTextDeleted().length() > 0) { + // backspace pressed + // erase a character in the file + fTextCommand.setTextDeleted(textDeleted.concat(fTextCommand.getTextDeleted())); + fTextCommand.setTextStart(textStart); + } + else { + createNewTextCommand(textDeleted, textInserted, textStart, textEnd); + } + + // save cursor position + fCursorPosition = textEnd; + } + + private void processStructuredDocumentEvent(StructuredDocumentEvent structuredDocumentEvent) { + // Note: fListening tells us if we should listen to the + // StructuredDocumentEvent. + // fListening is set to false right before the undo/redo process + // and + // then set to true again + // right after the undo/redo process to block out and ignore all + // StructuredDocumentEvents generated + // by the undo/redo process. + + // Process StructuredDocumentEvent if fListening is true. + // + // We are executing a command from the command stack if the + // requester + // is a command (for example, undo/redo). + // We should not process the flat model event when we are + // executing a + // command from the command stack. + if (fUndoManagementEnabled && !(structuredDocumentEvent.getOriginalRequester() instanceof Command)) { + // check requester if not recording + if (!fRecording) + checkRequester(structuredDocumentEvent.getOriginalRequester()); + + // process the structuredDocumentEvent + String textDeleted = structuredDocumentEvent.getDeletedText(); + String textInserted = structuredDocumentEvent.getText(); + int textStart = structuredDocumentEvent.getOffset(); + int textEnd = textStart + textInserted.length(); + processStructuredDocumentEvent(textDeleted, textInserted, textStart, textEnd); + } + } + + public void regionChanged(RegionChangedEvent structuredDocumentEvent) { + processStructuredDocumentEvent(structuredDocumentEvent); + } + + public void regionsReplaced(RegionsReplacedEvent structuredDocumentEvent) { + processStructuredDocumentEvent(structuredDocumentEvent); + } + + } + + private static final String TEXT_CHANGE_TEXT = SSECoreMessages.Text_Change_UI_; //$NON-NLS-1$ + private CommandStack fCommandStack = null; + private StructuredTextCompoundCommandImpl fCompoundCommand = null; + private String fCompoundCommandDescription = null; + private String fCompoundCommandLabel = null; + int fCursorPosition = 0; + // private IStructuredModel fStructuredModel = null; + private IDocument fDocument; + private InternalCommandStackListener fInternalCommandStackListener; + // private Map fTextViewerToListenerMap = new HashMap(); + private IStructuredDocumentListener fInternalStructuredDocumentListener; + private IDocumentSelectionMediator[] fMediators = null; + private boolean fRecording = false; + private int fRecordingCount = 0; + private Object fRequester; + StructuredTextCommandImpl fTextCommand = null; + private int fUndoCursorPosition = -1; + boolean fUndoManagementEnabled = true; + private int fUndoSelectionLength = 0; + + public StructuredTextUndoManager() { + this(new BasicCommandStack()); + } + + public StructuredTextUndoManager(CommandStack commandStack) { + setCommandStack(commandStack); + } + + private void addDocumentSelectionMediator(IDocumentSelectionMediator mediator) { + if (!Utilities.contains(fMediators, mediator)) { + int oldSize = 0; + + if (fMediators != null) { + // normally won't be null, but we need to be sure, for first + // time through + oldSize = fMediators.length; + } + + int newSize = oldSize + 1; + IDocumentSelectionMediator[] newMediators = new IDocumentSelectionMediator[newSize]; + if (fMediators != null) { + System.arraycopy(fMediators, 0, newMediators, 0, oldSize); + } + + // add the new undo mediator to last position + newMediators[newSize - 1] = mediator; + + // now switch new for old + fMediators = newMediators; + } + else { + removeDocumentSelectionMediator(mediator); + addDocumentSelectionMediator(mediator); + } + } + + public void beginRecording(Object requester) { + beginRecording(requester, null, null); + } + + public void beginRecording(Object requester, int cursorPosition, int selectionLength) { + beginRecording(requester, null, null); + + fUndoCursorPosition = cursorPosition; + fUndoSelectionLength = selectionLength; + } + + public void beginRecording(Object requester, String label) { + beginRecording(requester, label, null); + } + + public void beginRecording(Object requester, String label, int cursorPosition, int selectionLength) { + beginRecording(requester, label, null); + + fUndoCursorPosition = cursorPosition; + fUndoSelectionLength = selectionLength; + } + + public void beginRecording(Object requester, String label, String description) { + // save the requester + fRequester = requester; + + // update label and desc only on the first level when recording is + // nested + if (fRecordingCount == 0) { + fCompoundCommandLabel = label; + if (fCompoundCommandLabel == null) + fCompoundCommandLabel = TEXT_CHANGE_TEXT; + + fCompoundCommandDescription = description; + if (fCompoundCommandDescription == null) + fCompoundCommandDescription = TEXT_CHANGE_TEXT; + + // clear commands + fTextCommand = null; + fCompoundCommand = null; + } + + // update counter and flag + fRecordingCount++; + fRecording = true; + + // no undo cursor position and undo selection length specified + // reset undo cursor position and undo selection length + fUndoCursorPosition = -1; + fUndoSelectionLength = 0; + } + + public void beginRecording(Object requester, String label, String description, int cursorPosition, int selectionLength) { + beginRecording(requester, label, description); + + fUndoCursorPosition = cursorPosition; + fUndoSelectionLength = selectionLength; + } + + void checkRequester(Object requester) { + if (fRequester != null && !fRequester.equals(requester)) { + // Force restart of recording so the last compound command is + // closed. + // + // However, we should not force restart of recording when the + // request came from StructuredDocumentToTextAdapter or + // XMLModelImpl + // because cut/paste requests and character inserts to the + // textViewer are from StructuredDocumentToTextAdapter, + // and requests to delete a node in the XMLTableTreeViewer are + // from XMLModelImpl (which implements IStructuredModel). + + if (!(requester instanceof IStructuredModel || requester instanceof IStructuredDocument)) { + resetInternalCommands(); + } + } + } + + + + public void connect(IDocumentSelectionMediator mediator) { + Assert.isNotNull(mediator); + if (fDocument == null) { + // add this undo manager as structured document listener + fDocument = mediator.getDocument(); + // future_TODO: eventually we want to refactor or allow either + // type of document, but for now, we'll do instanceof check, and + // fail + // if not right type + if (fDocument instanceof IStructuredDocument) { + ((IStructuredDocument) fDocument).addDocumentChangedListener(getInternalStructuredDocumentListener()); + } + else { + throw new IllegalArgumentException("only meditator with structured documents currently handled"); //$NON-NLS-1$ + } + } + else { + // if we've already had our document set, we'll just do this fail + // fast integrity check + if (!fDocument.equals(mediator.getDocument())) + throw new IllegalStateException("Connection to undo manager failed. Document for document selection mediator inconistent with undo manager."); //$NON-NLS-1$ + } + + addDocumentSelectionMediator(mediator); + } + + void createNewTextCommand(String textDeleted, String textInserted, int textStart, int textEnd) { + StructuredTextCommandImpl textCommand = new StructuredTextCommandImpl(fDocument); + textCommand.setLabel(TEXT_CHANGE_TEXT); + textCommand.setDescription(TEXT_CHANGE_TEXT); + textCommand.setTextStart(textStart); + textCommand.setTextEnd(textEnd); + textCommand.setTextDeleted(textDeleted); + textCommand.setTextInserted(textInserted); + + if (fRecording) { + if (fCompoundCommand == null) { + StructuredTextCompoundCommandImpl compoundCommand = new StructuredTextCompoundCommandImpl(); + compoundCommand.setUndoCursorPosition(fUndoCursorPosition); + compoundCommand.setUndoSelectionLength(fUndoSelectionLength); + + compoundCommand.setLabel(fCompoundCommandLabel); + compoundCommand.setDescription(fCompoundCommandDescription); + compoundCommand.append(textCommand); + + fCompoundCommand = compoundCommand; + } + else { + fCompoundCommand.append(textCommand); + } + } + else { + fCommandStack.execute(textCommand); + } + + fTextCommand = textCommand; + } + + /** + * Disable undo management. + */ + public void disableUndoManagement() { + fUndoManagementEnabled = false; + } + + public void disconnect(IDocumentSelectionMediator mediator) { + removeDocumentSelectionMediator(mediator); + + if (fMediators != null && fMediators.length == 0 && fDocument != null) { + // remove this undo manager as structured document listener + // future_TODO: eventually we want to refactor or allow either + // type of document, but for now, we'll do instanceof check, and + // fail + // if not right type + if (fDocument instanceof IStructuredDocument) { + ((IStructuredDocument) fDocument).removeDocumentChangedListener(getInternalStructuredDocumentListener()); + } + else { + throw new IllegalArgumentException("only meditator with structured documents currently handled"); //$NON-NLS-1$ + } + // if no longer listening to document, then dont even track it + // anymore + // (this allows connect to reconnect to document again) + fDocument = null; + } + } + + public void enableUndoManagement() { + fUndoManagementEnabled = true; + } + + public void endRecording(Object requester) { + int cursorPosition = (fTextCommand != null) ? fTextCommand.getTextEnd() : -1; + int selectionLength = 0; + + endRecording(requester, cursorPosition, selectionLength); + } + + public void endRecording(Object requester, int cursorPosition, int selectionLength) { + // Recording could be stopped by forceEndOfPendingCommand(). Make sure + // we are still recording before proceeding, or else fRecordingCount + // may not be balanced. + if (fRecording) { + if (fCompoundCommand != null) { + fCompoundCommand.setRedoCursorPosition(cursorPosition); + fCompoundCommand.setRedoSelectionLength(selectionLength); + } + + // end recording is a logical stopping point for text command, + // even when fRecordingCount > 0 (in nested beginRecording) + fTextCommand = null; + + // update counter and flag + if (fRecordingCount > 0) + fRecordingCount--; + if (fRecordingCount == 0) { + + // Finally execute the commands accumulated in the compound command. + + if (fCompoundCommand != null) { + fCommandStack.execute(fCompoundCommand); + } + + fRecording = false; + + // reset compound command only when fRecordingCount == + // 0 + fCompoundCommand = null; + fCompoundCommandLabel = null; + fCompoundCommandDescription = null; + + // Also reset fRequester + fRequester = null; + } + } + } + + /** + * Utility method to find model given document + */ + private IStructuredModel findStructuredModel(IDocument document) { + IModelManager modelManager = StructuredModelManager.getModelManager(); + IStructuredModel structuredModel = modelManager.getExistingModelForRead(document); + return structuredModel; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.undo.IStructuredTextUndoManager#forceEndOfPendingCommand(java.lang.Object, + * int, int) + */ + public void forceEndOfPendingCommand(Object requester, int currentPosition, int length) { + if (fRecording) + endRecording(requester, currentPosition, length); + else + resetInternalCommands(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.undo.IStructuredTextUndoManager#getCommandStack() + */ + public CommandStack getCommandStack() { + return fCommandStack; + } + + /** + * @return + */ + private CommandStackListener getInternalCommandStackListener() { + if (fInternalCommandStackListener == null) { + fInternalCommandStackListener = new InternalCommandStackListener(); + } + return fInternalCommandStackListener; + } + + /** + * @return + */ + private IStructuredDocumentListener getInternalStructuredDocumentListener() { + if (fInternalStructuredDocumentListener == null) { + fInternalStructuredDocumentListener = new InternalStructuredDocumentListener(); + } + return fInternalStructuredDocumentListener; + } + + public Command getRedoCommand() { + return fCommandStack.getRedoCommand(); + } + + public Command getUndoCommand() { + return fCommandStack.getUndoCommand(); + } + + public void redo() { + redo(null); + } + + public void redo(IDocumentSelectionMediator requester) { + IStructuredModel model = findStructuredModel(fDocument); + + if (redoable()) { + IDocumentExtension4 docExt4 = null; + DocumentRewriteSession rewriteSession = null; + try { + if (model != null) + model.aboutToChangeModel(); + + Command redoCommand = getRedoCommand(); + if (redoCommand instanceof CompoundCommand && + model.getStructuredDocument() instanceof IDocumentExtension4) { + docExt4 = (IDocumentExtension4)model.getStructuredDocument(); + } + rewriteSession = (docExt4 == null) ? null : + docExt4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); + + // make sure to redo before setting document selection + fCommandStack.redo(); + + // set document selection + setRedoDocumentSelection(requester, redoCommand); + } + finally { + if (docExt4 != null && rewriteSession != null) + docExt4.stopRewriteSession(rewriteSession); + if (model != null) { + model.changedModel(); + model.releaseFromRead(); + } + } + } + } + + public boolean redoable() { + return fCommandStack.canRedo(); + } + + private void removeDocumentSelectionMediator(IDocumentSelectionMediator mediator) { + if (fMediators != null && mediator != null) { + // if its not in the array, we'll ignore the request + if (Utilities.contains(fMediators, mediator)) { + int oldSize = fMediators.length; + int newSize = oldSize - 1; + IDocumentSelectionMediator[] newMediators = new IDocumentSelectionMediator[newSize]; + int index = 0; + for (int i = 0; i < oldSize; i++) { + if (fMediators[i] == mediator) { // ignore + } + else { + // copy old to new if its not the one we are removing + newMediators[index++] = fMediators[i]; + } + } + // now that we have a new array, let's switch it for the old + // one + fMediators = newMediators; + } + } + } + + void resetInternalCommands() { + // Either the requester of the structured document change event is + // changed, or the command stack is changed. Need to reset internal + // commands so we won't continue to append changes. + fCompoundCommand = null; + fTextCommand = null; + + // Also reset fRequester + fRequester = null; + } + + public void setCommandStack(CommandStack commandStack) { + if (fCommandStack != null) + fCommandStack.removeCommandStackListener(getInternalCommandStackListener()); + + fCommandStack = commandStack; + + if (fCommandStack != null) + fCommandStack.addCommandStackListener(getInternalCommandStackListener()); + } + + private void setRedoDocumentSelection(IDocumentSelectionMediator requester, Command command) { + int cursorPosition = -1; + int selectionLength = 0; + + if (command instanceof CommandCursorPosition) { + CommandCursorPosition commandCursorPosition = (CommandCursorPosition) command; + cursorPosition = commandCursorPosition.getRedoCursorPosition(); + selectionLength = commandCursorPosition.getRedoSelectionLength(); + } + else if (command instanceof StructuredTextCommand) { + StructuredTextCommand structuredTextCommand = (StructuredTextCommand) command; + cursorPosition = structuredTextCommand.getTextStart(); + selectionLength = structuredTextCommand.getTextInserted().length(); + } + + if (cursorPosition > -1 && fMediators != null && fMediators.length > 0) { + for (int i = 0; i < fMediators.length; i++) { + IDocument document = fMediators[i].getDocument(); + fMediators[i].undoOperationSelectionChanged(new UndoDocumentEvent(requester, document, cursorPosition, selectionLength)); + } + } + } + + private void setUndoDocumentSelection(IDocumentSelectionMediator requester, Command command) { + int cursorPosition = -1; + int selectionLength = 0; + + if (command instanceof CommandCursorPosition) { + CommandCursorPosition commandCursorPosition = (CommandCursorPosition) command; + cursorPosition = commandCursorPosition.getUndoCursorPosition(); + selectionLength = commandCursorPosition.getUndoSelectionLength(); + } + else if (command instanceof StructuredTextCommand) { + StructuredTextCommand structuredTextCommand = (StructuredTextCommand) command; + cursorPosition = structuredTextCommand.getTextStart(); + selectionLength = structuredTextCommand.getTextDeleted().length(); + } + + if (cursorPosition > -1 && fMediators != null && fMediators.length > 0) { + for (int i = 0; i < fMediators.length; i++) { + IDocument document = fMediators[i].getDocument(); + fMediators[i].undoOperationSelectionChanged(new UndoDocumentEvent(requester, document, cursorPosition, selectionLength)); + } + } + } + + public void undo() { + undo(null); + } + + public void undo(IDocumentSelectionMediator requester) { + // Force an endRecording before undo. + // + // For example, recording was turned on on the Design Page of + // PageDesigner. + // Then undo is invoked on the Source Page. Recording should be + // stopped before we undo. + // Note that redo should not be available when we switch to the Source + // Page. + // Therefore, this force ending of recording is not needed in redo. + if (fRecording) + endRecording(this); + + if (undoable()) { + IStructuredModel model = findStructuredModel(fDocument); + IDocumentExtension4 docExt4 = null; + DocumentRewriteSession rewriteSession = null; + + try { + if (model != null) + model.aboutToChangeModel(); + + Command undoCommand = getUndoCommand(); + if (undoCommand instanceof CompoundCommand && + model.getStructuredDocument() instanceof IDocumentExtension4) { + docExt4 = (IDocumentExtension4)model.getStructuredDocument(); + } + rewriteSession = (docExt4 == null) ? null : + docExt4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); + + // make sure to undo before setting document selection + fCommandStack.undo(); + + // set document selection + setUndoDocumentSelection(requester, undoCommand); + } + finally { + if (docExt4 != null && rewriteSession != null) + docExt4.stopRewriteSession(rewriteSession); + if (model != null) { + model.changedModel(); + model.releaseFromRead(); + } + } + } + } + + public boolean undoable() { + return fCommandStack.canUndo(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/UndoDocumentEvent.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/UndoDocumentEvent.java new file mode 100644 index 0000000..0fee467 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/undo/UndoDocumentEvent.java
@@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.undo; + +import org.eclipse.jface.text.IDocument; + +public class UndoDocumentEvent { + private IDocument fDocument; + private int fLength; + private int fOffset; + private IDocumentSelectionMediator fRequester; + + public UndoDocumentEvent(IDocumentSelectionMediator requester, IDocument document, int offset, int length) { + fRequester = requester; + fDocument = document; + fOffset = offset; + fLength = length; + } + + public IDocument getDocument() { + return fDocument; + } + + public int getLength() { + return fLength; + } + + public int getOffset() { + return fOffset; + } + + public IDocumentSelectionMediator getRequester() { + return fRequester; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/AbstractMemoryListener.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/AbstractMemoryListener.java new file mode 100644 index 0000000..46c60a8 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/AbstractMemoryListener.java
@@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 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.wst.sse.core.internal.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECorePlugin; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; + +/** + * This responds to memory events. + * + * Create an instance of a child of this class with the events you are interested in. + * Then call connect() to start listening. To stop listening call disconnect(); + */ +public abstract class AbstractMemoryListener implements EventHandler { + /** + * The event that indicates that memory is running low at the lowest severity. + * Listeners are requested to release caches that can easily be recomputed. + * The Java VM is not seriously in trouble, but process size is getting higher than + * is deemed acceptable. + */ + public static final String SEV_NORMAL = "org/eclipse/equinox/events/MemoryEvent/NORMAL"; //$NON-NLS-1$ + + /** + * The event that indicates that memory is running low at medium severity. + * Listeners are requested to release intermediate build results, complex models, etc. + * Memory is getting low and may cause operating system level stress, such as swapping. + */ + public static final String SEV_SERIOUS = "org/eclipse/equinox/events/MemoryEvent/SERIOUS"; //$NON-NLS-1$ + + /** + * The event that indicates that memory is running low at highest severity. + * Listeners are requested to do things like close editors and perspectives, close database connections, etc. + * Restoring these resources and caches constitutes lots of work, but memory is so low that + * drastic measures are required. + */ + public static final String SEV_CRITICAL = "org/eclipse/equinox/events/MemoryEvent/CRITICAL"; //$NON-NLS-1$ + + /** + * All of the valid memory severities + */ + public static final String[] SEV_ALL = { SEV_NORMAL, SEV_SERIOUS, SEV_CRITICAL }; + + /** + * Used to register the {@link EventAdmin} listener + */ + private static BundleContext CONTEXT = + (SSECorePlugin.getDefault() != null) ? + SSECorePlugin.getDefault().getBundle().getBundleContext() : null; + + /** + * the severities that will be reacted to + */ + private final List fSeverities; + + /** + * service used to register this listener + */ + private ServiceRegistration fRegisterService; + + /** + * Will listen to all memory events + */ + public AbstractMemoryListener() { + this(AbstractMemoryListener.SEV_ALL); + } + + /** + * Will listen to memory events of the given <code>severity</code> + * + * @param severity listen for memory events of this severity + */ + public AbstractMemoryListener(String severity) { + Assert.isNotNull(severity, "Severity can not be null"); //$NON-NLS-1$ + + List severities = new ArrayList(1); + severities.add(severity); + fSeverities = severities; + } + + /** + * Will listen to memory events of the given <code>severities</code> + * + * @param severities listen for memory events for any of these severities + */ + public AbstractMemoryListener(String[] severities) { + Assert.isNotNull(severities, "Severities can not be null"); //$NON-NLS-1$ + Assert.isLegal(severities.length > 0, "Severities must specify at least one severity"); //$NON-NLS-1$ + + fSeverities = Arrays.asList(severities); + } + + /** + * Will listen to memory events of the given <code>severities</code> + * + * @param severities listen for memory events for any of these severities + */ + public AbstractMemoryListener(List severities) { + Assert.isNotNull(severities, "Severities can not be null"); //$NON-NLS-1$ + Assert.isLegal(!severities.isEmpty(), "Severities must specify at least one severity"); //$NON-NLS-1$ + fSeverities = severities; + } + + /** + * Connect this listener to the {@link EventAdmin} + */ + public final void connect() { + if (CONTEXT != null) { + // NOTE: This is TEMPORARY CODE needed to load the plugin + // until its done automatically by the product + // TODO: Remove me + Bundle b = Platform.getBundle("org.eclipse.equinox.event"); //$NON-NLS-1$ + if (b != null && b.getState() == Bundle.RESOLVED) { + try { + b.start(Bundle.START_TRANSIENT); + } + catch (BundleException e) { + e.printStackTrace(); + } + } + // end remove me + + //register this handler + String[] severities = (String[])fSeverities.toArray(new String[fSeverities.size()]); + Hashtable prop = new Hashtable(1); + prop.put(EventConstants.EVENT_TOPIC, severities); + fRegisterService = CONTEXT.registerService(EventHandler.class.getName(), this, prop); + + //call any implementer specific connect code + doConnect(); + } else { + Logger.log(Logger.WARNING, "Error accessing bundle context. Is Platform running? Not tracking memory events. "); //$NON-NLS-1$ + } + } + + /** + * Disconnect this listener to the {@link EventAdmin} + */ + public final void disconnect() { + if (fRegisterService != null) { + fRegisterService.unregister(); + fRegisterService = null; + } + + //call any implementer specific disconnect code + doDisconnect(); + } + + /** + * <p>Filter out any events that are not of the type that this listener handles</p> + * + * @see org.osgi.service.event.EventHandler#handleEvent(org.osgi.service.event.Event) + */ + public final void handleEvent(Event event) { + if (fSeverities.contains(event.getTopic())) { + handleMemoryEvent(event); + } + } + + /** + * Implementing child classes may assume that only {@link Event}s of the types + * given to the constructor will be given to this method. + * + * @param event the {@link Event} with a topic equal to one of the memory + * severities that this listener is listening for + */ + protected abstract void handleMemoryEvent(Event event); + + /** + * Implementers may overrun this method to do setup after connection of this listener + */ + protected void doConnect() { + //do nothing by default + } + + /** + * Implementers may overrun this method to do tear down after disconnection of this listener + */ + protected void doDisconnect() { + //do nothing by default + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Assert.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Assert.java new file mode 100644 index 0000000..f3d1ee0 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Assert.java
@@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + + +/** + * <code>Assert</code> is useful for for embedding runtime sanity checks in + * code. The predicate methods all test a condition and throw some type of + * unchecked exception if the condition does not hold. + * <p> + * Assertion failure exceptions, like most runtime exceptions, are thrown when + * something is misbehaving. Assertion failures are invariably unspecified + * behavior; consequently, clients should never rely on these being thrown + * (and certainly should not being catching them specifically). + * </p> + */ +public final class Assert { + + /** + * <code>AssertionFailedException</code> is a runtime exception thrown + * by some of the methods in <code>Assert</code>. + * <p> + * This class is not declared public to prevent some misuses; programs + * that catch or otherwise depend on assertion failures are susceptible to + * unexpected breakage when assertions in the code are added or removed. + * </p> + */ + class AssertionFailedException extends RuntimeException { + /** + * Comment for <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception. + */ + public AssertionFailedException() { + super(); + } + + /** + * Constructs a new exception with the given message. + */ + public AssertionFailedException(String detail) { + super(detail); + } + } + + /** + * Asserts that an argument is legal. If the given boolean is not + * <code>true</code>, an <code>IllegalArgumentException</code> is + * thrown. + * + * @param expression + * the outcode of the check + * @return <code>true</code> if the check passes (does not return if the + * check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression) { + return isLegal(expression, ""); //$NON-NLS-1$ + } + + /** + * Asserts that an argument is legal. If the given boolean is not + * <code>true</code>, an <code>IllegalArgumentException</code> is + * thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the outcode of the check + * @param message + * the message to include in the exception + * @return <code>true</code> if the check passes (does not return if the + * check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression, String message) { + if (!expression) + throw new IllegalArgumentException(); + return expression; + } + + /** + * Asserts that the given object is not <code>null</code>. If this is + * not the case, some kind of unchecked exception is thrown. + * + * @param object + * the value to test + * @exception IllegalArgumentException + * if the object is <code>null</code> + */ + public static void isNotNull(Object object) { + isNotNull(object, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given object is not <code>null</code>. If this is + * not the case, some kind of unchecked exception is thrown. The given + * message is included in that exception, to aid debugging. + * + * @param object + * the value to test + * @param message + * the message to include in the exception + * @exception IllegalArgumentException + * if the object is <code>null</code> + */ + public static void isNotNull(Object object, String message) { + if (object == null) { + //Logger.log(Logger.ERROR, "null_argument: " + message); //$NON-NLS-1$ + throw new Assert().new AssertionFailedException(message); + } + } + + /** + * Asserts that the given boolean is <code>true</code>. If this is not + * the case, some kind of unchecked exception is thrown. + * + * @param expression + * the outcode of the check + * @return <code>true</code> if the check passes (does not return if the + * check fails) + */ + public static boolean isTrue(boolean expression) { + return isTrue(expression, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given boolean is <code>true</code>. If this is not + * the case, some kind of unchecked exception is thrown. The given message + * is included in that exception, to aid debugging. + * + * @param expression + * the outcode of the check + * @param message + * the message to include in the exception + * @return <code>true</code> if the check passes (does not return if the + * check fails) + */ + public static boolean isTrue(boolean expression, String message) { + if (!expression) { + throw new Assert().new AssertionFailedException(message); + } + return expression; + } + + /* This class is not intended to be instantiated. */ + private Assert() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Debug.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Debug.java new file mode 100644 index 0000000..3df8954 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Debug.java
@@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300430 - String concatenation + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + + + +import java.util.Enumeration; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + + +public final class Debug { + public static final boolean checkForMemoryLeaks = false; + + public static final boolean collectStats = false; + + public static final int DEBUG = 0; + + public static final boolean DEBUG_THREADLOCAL = false; + + public static final boolean debugBreakpoints = false; + public static final boolean debugCaretMediator = false; + public static final boolean debugDisplayTreePositions = false; + // + public static final boolean debugMediator = false; + // + public static final boolean debugNotification = false; + public static final boolean debugNotificationAndEvents = false; + + public static final boolean debugNotifyDeferred = false; + public static final boolean debugReconciling = false; + // + public static final boolean debugRtfFormatProvider = false; + // + public static final boolean debugStructuredDocument = false; + public static final boolean debugTaglibs = false; + // + public static final boolean debugTokenizer = false; + // + public static final boolean debugTreeModel = false; + public static final boolean debugUpdateTreePositions = false; + public static final boolean displayInfo = false; + + /** effects output of Logger */ + public static final boolean displayToConsole = true; + public static final boolean displayWarnings = false; + // + public static final boolean headParsing = false; + public static final boolean jsDebugContextAssist = false; + // + public static final boolean jsDebugSyntaxColoring = false; + + public static final boolean LOCKS = false; + // + public static final boolean perfTest = false; + public static final boolean perfTestAdapterClassLoading = false; + public static final boolean perfTestFormat = false; + public static final boolean perfTestRawStructuredDocumentOnly = false; + public static final boolean perfTestStructuredDocumentEventOnly = false; + public static final boolean perfTestStructuredDocumentOnly = false; + + // + public static final boolean syntaxHighlighting = false; + // + public static final boolean useStandardEolInWidget = false; + + /** + * For tests and debug only + */ + + public static final void dump(IStructuredDocument structuredDocument) { + dump(structuredDocument, false); + } + + public static final void dump(IStructuredDocument structuredDocument, boolean verbose) { + ITextRegionCollection flatNode = null; + System.out.println("Dump of structuredDocument:"); //$NON-NLS-1$ + IStructuredDocumentRegionList flatNodes = structuredDocument.getRegionList(); + Enumeration structuredDocumentRegions = flatNodes.elements(); + while (structuredDocumentRegions.hasMoreElements()) { + flatNode = (ITextRegionCollection) structuredDocumentRegions.nextElement(); + if (!verbose) { + String outString = flatNode.toString(); + outString = org.eclipse.wst.sse.core.utils.StringUtils.escape(outString); + System.out.println(outString); + } else { + dump(flatNode, verbose); + } + } + System.out.println(); + System.out.println("= = = = = ="); //$NON-NLS-1$ + System.out.println(); + } + + /** + * @param flatNode + * @param verbose + */ + public static final void dump(ITextRegionCollection region, boolean verbose) { + if (region == null) + return; + if (verbose) { + printParent(region); + } + printChildRegions(region, 0); + } + + private static void printChildRegions(ITextRegionCollection region, int depth) { + if (region != null) { + // ==> // ITextRegionCollection regionCollection = region; + System.out.println(region); + ITextRegionList regionList = region.getRegions(); + for (int i = 0; i < regionList.size(); i++) { + ITextRegion r = regionList.get(i); + if (r instanceof ITextRegionCollection) { + ITextRegionCollection rc = (ITextRegionCollection) r; + printChildRegions(rc, depth++); + } else { + System.out.println(space(depth) + r); + depth--; + } + } + } + } + + /** + * Simple utility to make sure println's are some what in order + */ + public static final synchronized void println(String msg) { + System.out.println(System.currentTimeMillis() + "\t" + msg); //$NON-NLS-1$ + } + + private static void printParent(IStructuredDocumentRegion region) { + System.out.println(" [parent document: " + toStringUtil(region.getParentDocument()) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private static void printParent(ITextRegionCollection region) { + if (region instanceof IStructuredDocumentRegion) { + printParent((IStructuredDocumentRegion) region); + } else if (region instanceof ITextRegionContainer) { + printParent((ITextRegionContainer) region); + } else + System.out.println(" [parent document: " + "(na)" + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + private static void printParent(ITextRegionContainer region) { + System.out.println(" [parent document: " + toStringUtil(region.getParent()) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * @param depth + * @return + */ + private static String space(int depth) { + String result = " "; //$NON-NLS-1$ + StringBuffer sb = new StringBuffer(result); + for (int i = 0; i < depth; i++) { + sb.append(" "); //$NON-NLS-1$ + } + result = sb.toString(); + return result; + } + + public static final String toStringUtil(IStructuredDocument object) { + String className = object.getClass().getName(); + String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$ + String result = shortClassName; + // NOTE: if the document held by any region has been updated and the + // region offsets have not + // yet been updated, the output from this method invalid. + return result; + + } + + public static final String toStringUtil(ITextRegionCollection object) { + String className = object.getClass().getName(); + String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$ + String result = shortClassName; + // NOTE: if the document held by any region has been updated and the + // region offsets have not + // yet been updated, the output from this method invalid. + return result; + + } + + /** + * Debug constructor comment. + */ + public Debug() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/DocumentInputStream.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/DocumentInputStream.java new file mode 100644 index 0000000..fad0951 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/DocumentInputStream.java
@@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; + +public class DocumentInputStream extends InputStream { + private IDocument fDocument; + private int fMark = -1; + private int fPosition = 0; + + public DocumentInputStream(IDocument source) { + super(); + fDocument = source; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#available() + */ + public int available() throws IOException { + return fDocument.getLength() - fPosition; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + this.fDocument = null; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#mark(int) + */ + public synchronized void mark(int readlimit) { + fMark = fPosition; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#markSupported() + */ + public boolean markSupported() { + return true; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read() + */ + public int read() throws IOException { + try { + if (fPosition < fDocument.getLength()) + return fDocument.getChar(fPosition++); + else + return -1; + } catch (BadLocationException e) { + throw new IOException(e.getMessage()); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#reset() + */ + public synchronized void reset() throws IOException { + fPosition = fMark; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#skip(long) + */ + public long skip(long n) throws IOException { + long skipped = n; + if (n < fDocument.getLength() - fPosition) { + skipped = n; + fPosition += skipped; + } else { + skipped = fDocument.getLength() - fPosition; + fPosition = fDocument.getLength(); + } + return skipped; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/JarUtilities.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/JarUtilities.java new file mode 100644 index 0000000..d2bedbb --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/JarUtilities.java
@@ -0,0 +1,394 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.wst.sse.core.internal.Logger; + + +public class JarUtilities { + + /** + * @see http://java.sun.com/products/jsp/errata_1_1_a_042800.html, Issues + * 8 & 9 + * + * "There are two cases. In both cases the TLD_URI is to be + * interpreted relative to the root of the Web Application. In the + * first case the TLD_URI refers to a TLD file directly. In the + * second case, the TLD_URI refers to a JAR file. If so, that JAR + * file should have a TLD at location META-INF/taglib.tld." + */ + public static final String JSP11_TAGLIB = "META-INF/taglib.tld"; //$NON-NLS-1$ + + public static void closeJarFile(ZipFile file) { + if (file == null) + return; + try { + file.close(); + } + catch (IOException ioe) { + // no cleanup can be done + } + } + + /** + * Provides a stream to a local copy of the input or null if not possible + */ + protected static InputStream getCachedInputStream(String jarFilename, String entryName) { + File testFile = new File(jarFilename); + if (!testFile.exists()) + return getInputStream(ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(jarFilename)), entryName); + + InputStream cache = null; + ZipFile jarfile = null; + try { + jarfile = new ZipFile(jarFilename); + } + catch (IOException ioExc) { + closeJarFile(jarfile); + } + + if (jarfile != null) { + try { + ZipEntry zentry = jarfile.getEntry(entryName); + if (zentry != null) { + InputStream entryInputStream = null; + try { + entryInputStream = jarfile.getInputStream(zentry); + } + catch (IOException ioExc) { + // no cleanup can be done + } + + if (entryInputStream != null) { + int c; + ByteArrayOutputStream buffer = null; + if (zentry.getSize() > 0) { + buffer = new ByteArrayOutputStream((int) zentry.getSize()); + } + else { + buffer = new ByteArrayOutputStream(); + } + // array dim restriction? + byte bytes[] = new byte[2048]; + try { + while ((c = entryInputStream.read(bytes)) >= 0) { + buffer.write(bytes, 0, c); + } + cache = new ByteArrayInputStream(buffer.toByteArray()); + closeJarFile(jarfile); + } + catch (IOException ioe) { + // no cleanup can be done + } + finally { + try { + entryInputStream.close(); + } + catch (IOException e) { + // no cleanup can be done + } + } + } + } + } + finally { + closeJarFile(jarfile); + } + } + return cache; + } + + private static InputStream copyAndCloseStream(InputStream original) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + InputStream cachedCopy = null; + + if (original != null) { + int c; + // array dim restriction? + byte bytes[] = new byte[2048]; + try { + while ((c = original.read(bytes)) >= 0) { + buffer.write(bytes, 0, c); + } + cachedCopy = new ByteArrayInputStream(buffer.toByteArray()); + closeStream(original); + } + catch (IOException ioe) { + // no cleanup can be done + } + } + return cachedCopy; + } + + /** + * @param jarResource + * the zip file + * @return a string array containing the entry paths to every file in this + * zip resource, excluding directories + */ + public static String[] getEntryNames(IResource jarResource) { + if (jarResource == null || jarResource.getType() != IResource.FILE || !jarResource.isAccessible()) + return new String[0]; + + try { + return getEntryNames(jarResource.getFullPath().toString(), new ZipInputStream(((IFile) jarResource).getContents()), true); + } + catch (CoreException e) { + // no cleanup can be done + } + + IPath location = jarResource.getLocation(); + if (location != null) + return getEntryNames(location.toString()); + return new String[0]; + } + + /** + * @param jarFilename + * the location of the zip file + * @return a string array containing the entry paths to every file in the + * zip file at this location, excluding directories + */ + public static String[] getEntryNames(String jarFilename) { + return getEntryNames(jarFilename, true); + } + + private static String[] getEntryNames(String filename, ZipInputStream jarInputStream, boolean excludeDirectories) { + List entryNames = new ArrayList(); + try { + ZipEntry z = jarInputStream.getNextEntry(); + while (z != null) { + if (!(z.isDirectory() && excludeDirectories)) + entryNames.add(z.getName()); + z = jarInputStream.getNextEntry(); + } + } + catch (ZipException zExc) { + Logger.log(Logger.WARNING_DEBUG, "JarUtilities ZipException: (stream) " + filename, zExc); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (IOException ioExc) { + // no cleanup can be done + } + finally { + closeStream(jarInputStream); + } + String[] names = (String[]) entryNames.toArray(new String[0]); + return names; + } + + private static void closeStream(InputStream inputStream) { + try { + inputStream.close(); + } + catch (IOException e) { + // no cleanup can be done + } + } + + /** + * @param jarFilename + * the location of the zip file + * @param excludeDirectories + * whether to not include directories in the results + * @return a string array containing the entry paths to every file in the + * zip file at this location, excluding directories if indicated + */ + public static String[] getEntryNames(String jarFilename, boolean excludeDirectories) { + ZipFile jarfile = null; + List entryNames = new ArrayList(); + File f = new File(jarFilename); + if (f.exists() && f.canRead()) { + try { + jarfile = new ZipFile(f); + Enumeration entries = jarfile.entries(); + while (entries.hasMoreElements()) { + ZipEntry z = (ZipEntry) entries.nextElement(); + if (!(z.isDirectory() && excludeDirectories)) + entryNames.add(z.getName()); + } + } + catch (ZipException zExc) { + Logger.log(Logger.WARNING_DEBUG, "JarUtilities ZipException: " + jarFilename + " " + zExc.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (IOException ioExc) { + // no cleanup can be done + } + finally { + closeJarFile(jarfile); + } + } + String[] names = (String[]) entryNames.toArray(new String[0]); + return names; + } + + /** + * @param jarResource + * the zip file + * @param entryName + * the entry's path in the zip file + * @return an InputStream to the contents of the given entry or null if + * not possible + */ + public static InputStream getInputStream(IResource jarResource, String entryName) { + if (jarResource == null || jarResource.getType() != IResource.FILE || !jarResource.isAccessible()) + return null; + + try { + InputStream zipStream = ((IFile) jarResource).getContents(); + return getInputStream(jarResource.getFullPath().toString(), new ZipInputStream(zipStream), entryName); + } + catch (CoreException e) { + // no cleanup can be done, probably out of sync + } + + IPath location = jarResource.getLocation(); + if (location != null) { + return getInputStream(location.toString(), entryName); + } + return null; + } + + private static InputStream getInputStream(String filename, ZipInputStream zip, String entryName) { + InputStream result = null; + try { + ZipEntry z = zip.getNextEntry(); + while (z != null && !z.getName().equals(entryName)) { + z = zip.getNextEntry(); + } + if (z != null) { + result = copyAndCloseStream(zip); + } + } + catch (ZipException zExc) { + Logger.log(Logger.WARNING_DEBUG, "JarUtilities ZipException: (stream) " + filename, zExc); //$NON-NLS-1$ //$NON-NLS-2$ + } + catch (IOException ioExc) { + // no cleanup can be done + } + finally { + closeStream(zip); + } + return result; + } + + /** + * @param jarFilename + * the location of the zip file + * @param entryName + * the entry's path in the zip file + * @return an InputStream to the contents of the given entry or null if + * not possible + */ + public static InputStream getInputStream(String jarFilename, String entryName) { + // check sanity + if (jarFilename == null || jarFilename.length() < 1 || entryName == null || entryName.length() < 1) + return null; + + // JAR files are not allowed to have leading '/' in member names + String internalName = null; + if (entryName.startsWith("/")) //$NON-NLS-1$ + internalName = entryName.substring(1); + else + internalName = entryName; + + return getCachedInputStream(jarFilename, internalName); + } + + /** + * @param url + * a URL pointint to a zip file + * @return a cached copy of the contents at this URL, opening it as a file + * if it is a jar:file: URL, and using a URLConnection otherwise, + * or null if it could not be read. All sockets and file handles + * are closed as quickly as possible. + */ + public static InputStream getInputStream(URL url) { + String urlString = url.toString(); + if (urlString.length() > 12 && urlString.startsWith("jar:file:") && urlString.indexOf("!/") > 9) { //$NON-NLS-1$ //$NON-NLS-2$ + int fileIndex = urlString.indexOf("!/"); //$NON-NLS-1$ + String jarFileName = urlString.substring(9, fileIndex); + if (fileIndex < urlString.length()) { + String jarPath = urlString.substring(fileIndex + 1); + return getInputStream(jarFileName, jarPath); + } + } + + InputStream input = null; + JarURLConnection jarUrlConnection = null; + try { + URLConnection openConnection = url.openConnection(); + openConnection.setDefaultUseCaches(false); + openConnection.setUseCaches(false); + if (openConnection instanceof JarURLConnection) { + jarUrlConnection = (JarURLConnection) openConnection; + JarFile jarFile = jarUrlConnection.getJarFile(); + input = jarFile.getInputStream(jarUrlConnection.getJarEntry()); + } + else { + input = openConnection.getInputStream(); + } + if (input != null) { + return copyAndCloseStream(input); + } + } + catch (FileNotFoundException e) { + // May be a file URL connection, do not log + } + catch (IOException e) { + Logger.logException(e); + } + finally { + if (jarUrlConnection != null) { + try { + jarUrlConnection.getJarFile().close(); + } + catch (IOException e) { + // ignore + } + catch (IllegalStateException e) { + /* + * ignore. Can happen in case the stream.close() did close + * the jar file see + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=140750 + */ + } + + } + } + return null; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/PathHelper.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/PathHelper.java new file mode 100644 index 0000000..b835085 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/PathHelper.java
@@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300430 - String concatenation + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + + + +import java.io.File; +import com.ibm.icu.util.StringTokenizer; + +/** + * Collection of helper methods to manage and convert links Originally part of + * the LinksManager + */ +public class PathHelper { + public static final String BACKWARD_SLASH = "\\";//$NON-NLS-1$ + + public static final String FORWARD_SLASH = "/";//$NON-NLS-1$ + public static final String RELATIVE_PATH_SIG = "../";//$NON-NLS-1$ + + /** + * adjust relative path isside the absolute path + */ + public static String adjustPath(String path) { + int i = 0; + while ((i = path.indexOf(RELATIVE_PATH_SIG)) > 0) { + // split the string into two + String part1 = path.substring(0, i - 1); + String part2 = path.substring(i + RELATIVE_PATH_SIG.length() - 1); + // strip one path seg from part1 + int j = part1.lastIndexOf(FORWARD_SLASH); + if (j == -1) { + // can't resolve. passed path is like + // E:/eclipseproject/../../sample.css. + return "";//$NON-NLS-1$ + } + part1 = part1.substring(0, j); + path = part1 + part2; + } + return path; + } + + /** + * Append trailing url slash if needed + */ + public static String appendTrailingURLSlash(String input) { + // check to see already a slash + if (!input.endsWith(FORWARD_SLASH)) { + input += FORWARD_SLASH; + } + return input; + } + + /** + * Convert to relative url based on base + */ + public static String convertToRelative(String input, String base) { + // tokenize the strings + StringTokenizer inputTokenizer = new StringTokenizer(input, FORWARD_SLASH); + StringTokenizer baseTokenizer = new StringTokenizer(base, FORWARD_SLASH); + String token1 = "", token2 = "";//$NON-NLS-2$//$NON-NLS-1$ + // + // Go through until equls + while (true) { + if (!inputTokenizer.hasMoreTokens() || !baseTokenizer.hasMoreTokens()) + break; + token1 = baseTokenizer.nextToken(); + token2 = inputTokenizer.nextToken(); + if (!token1.equals(token2)) + break; + } + // now generate the backs + String output = "";//$NON-NLS-1$ + StringBuffer sb = new StringBuffer(output); + while (baseTokenizer.hasMoreTokens()) { + baseTokenizer.nextToken(); + sb.append("../"); //$NON-NLS-1$ + } + sb.append(token2); + // generate the rest + while (inputTokenizer.hasMoreTokens()) { + sb.append(FORWARD_SLASH); + sb.append(inputTokenizer.nextToken()); + } + output = sb.toString(); + return output; + } + + /** + * Return the containing folder path. Will handle both url and file path + */ + public static String getContainingFolderPath(String path) { + String retValue = path; + + int urlSlashIndex = path.lastIndexOf(FORWARD_SLASH); + int filePathSlashIndex = path.lastIndexOf(File.separator); + int index = filePathSlashIndex; + if (urlSlashIndex > filePathSlashIndex) + index = urlSlashIndex; + if (index >= 0) + retValue = path.substring(0, index); + return retValue; + } + + /** + * Remove leading path separator + */ + public static String removeLeadingPathSeparator(String path) { + if (path.startsWith(File.separator)) + path = path.substring(File.separator.length()); + return path; + } + + /** + * Remove leading path separator + */ + public static String removeLeadingSeparator(String path) { + if (path.startsWith(File.separator)) + path = path.substring(File.separator.length()); + else if (path.startsWith(FORWARD_SLASH) || path.startsWith(BACKWARD_SLASH)) + path = path.substring(FORWARD_SLASH.length()); + return path; + } + + /** + * Switch to file path slashes + */ + public static String switchToFilePathSlashes(String path) { + path = path.replace(FORWARD_SLASH.charAt(0), File.separatorChar); + path = path.replace(BACKWARD_SLASH.charAt(0), File.separatorChar); + return path; + } + + /** + * Switch to file path slashes + */ + public static String switchToForwardSlashes(String path) { + path = path.replace(File.separatorChar, FORWARD_SLASH.charAt(0)); + path = path.replace(BACKWARD_SLASH.charAt(0), FORWARD_SLASH.charAt(0)); + return path; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ProjectResolver.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ProjectResolver.java new file mode 100644 index 0000000..0a21f95 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ProjectResolver.java
@@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright (c) 2001, 2007 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.wst.common.uriresolver.internal.util.URIHelper; + +import com.ibm.icu.util.StringTokenizer; + +/** + * @deprecated The URIResolver interface is deprecated. Use the resolver from + * org.eclipse.wst.common.uriresolver. + */ +public class ProjectResolver implements URIResolver { + private String fFileBaseLocation = null; + private IProject fProject = null; + + /** + * It is strongly recommended that clients use + * project.getAdapter(URIResolver.class) to obtain a URIResolver aware of + * the Project's special requirements. Note that a URIResolver may not be + * returned at all so manually creating this object may still be required. + */ + public ProjectResolver(IProject project) { + super(); + fProject = project; + } + + public String getFileBaseLocation() { + return fFileBaseLocation; + } + + public String getLocationByURI(String uri) { + return getLocationByURI(uri, getFileBaseLocation()); + } + + // defect 244817 end + /** + * Resolve the (possibly relative) URI acording to RFC1808 using the + * default file base location. Resolves resource references into absolute + * resource locations without ensuring that the resource actually exists. + * + * Note: currently resolveCrossProjectLinks is ignored in this + * implementation. + */ + public String getLocationByURI(String uri, boolean resolveCrossProjectLinks) { + return getLocationByURI(uri, getFileBaseLocation(), resolveCrossProjectLinks); + } + + public String getLocationByURI(String uri, String baseReference) { + if (uri == null) + return null; + /* + * defect 244817 try { URL aURL = new URL(uri); + */ + /** + * An actual URL was given, but only the "file:///" protocol is + * supported. Resolve the URI by finding the file to which it points. + */ + /* + * defect 244817 if (!aURL.getProtocol().equals("platform")) { + * //$NON-NLS-1$ if (aURL.getProtocol().equals("file") && + * (aURL.getHost().equals("localhost") || aURL.getHost().length() == + * 0)) { //$NON-NLS-2$//$NON-NLS-1$ return aURL.getFile(); } return + * uri; } } catch (MalformedURLException mfuExc) { } + */ + // defect 244817 start + if (isFileURL(uri)) { + try { + URL url = new URL(uri); + return getPath(url); + } + catch (MalformedURLException e) { + } + } + // defect 244817 end + + // which of the serveral are we suppose to use here? + // + + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=71223 + // Workaround for problem in URIHelper; uris starting with '/' are + // returned as-is. + String location = null; + if (uri.startsWith("/")) { //$NON-NLS-1$ + IProject p = getProject(); + if (p != null && p.isAccessible()) { + IFile file = p.getFile(uri); + + if (file.getLocation() != null) { + location = file.getLocation().toString(); + } + if (location == null && file.getLocationURI() != null) { + location = file.getLocationURI().toString(); + } + if (location == null) { + location = file.getFullPath().toString(); + } + } + } + if(location == null) { + location = URIHelper.normalize(uri, baseReference, getRootLocationString()); + } + return location; + } + + /** + * Perform the getLocationByURI action using the baseReference as the + * point of reference instead of the default for this resolver + * + * Note: currently resolveCrossProjectLinks is ignored in this + * implementation. + */ + public String getLocationByURI(String uri, String baseReference, boolean resolveCrossProjectLinks) { + return getLocationByURI(uri, baseReference); + } + + /** + * + * @param path + * @param host + * @return String + */ + private String getPath(IPath path, String host) { + IPath newPath = path; + // They are potentially for only Windows operating system. + // a.) if path has a device, and if it begins with IPath.SEPARATOR, + // remove it + final String device = path.getDevice(); + if ((device != null) && (device.length() > 0)) { + if (device.charAt(0) == IPath.SEPARATOR) { + final String newDevice = device.substring(1); + newPath = path.setDevice(newDevice); + } + } + // b.) if it has a hostname, it is UNC name... Any java or eclipse api + // helps it ?? + if (path != null && host != null && host.length() != 0) { + IPath uncPath = new Path(host); + uncPath = uncPath.append(path); + newPath = uncPath.makeUNC(true); + } + return newPath.toString(); + } + + /** + * + * @param url + * @return String + */ + private String getPath(URL url) { + String ref = url.getRef() == null ? "" : "#" + url.getRef(); //$NON-NLS-1$ //$NON-NLS-2$ + String strPath = url.getFile() + ref; + IPath path; + if (strPath.length() == 0) { + path = Path.ROOT; + } + else { + path = new Path(strPath); + String query = null; + StringTokenizer parser = new StringTokenizer(strPath, "?"); //$NON-NLS-1$ + int tokenCount = parser.countTokens(); + if (tokenCount == 2) { + path = new Path((String) parser.nextElement()); + query = (String) parser.nextElement(); + } + if (query == null) { + parser = new StringTokenizer(path.toString(), "#"); //$NON-NLS-1$ + tokenCount = parser.countTokens(); + if (tokenCount == 2) { + path = new Path((String) parser.nextElement()); + } + } + } + return getPath(path, url.getHost()); + } + + public org.eclipse.core.resources.IProject getProject() { + return fProject; + } + + public org.eclipse.core.resources.IContainer getRootLocation() { + return fProject; + } + + protected String getRootLocationString() { + String location = null; + if (fProject == null) + return null; + if (fProject.getLocation() != null) { + location = fProject.getLocation().toString(); + } + if (location == null && fProject.getLocationURI() != null) { + location = fProject.getLocationURI().toString(); + } + if (location == null) { + location = fProject.getFullPath().toString(); + } + return location; + } + + public InputStream getURIStream(String uri) { + return null; + } + + // defect 244817 start + /** + * + * @param passedSpec + * @return boolean + */ + private boolean isFileURL(String passedSpec) { + if (passedSpec == null) { + return false; + } + final String spec = passedSpec.trim(); + if (spec.length() == 0) { + return false; + } + final int limit = spec.length(); + String newProtocol = null; + for (int index = 0; index < limit; index++) { + final char p = spec.charAt(index); + if (p == '/') { //$NON-NLS-1$ + break; + } + if (p == ':') { //$NON-NLS-1$ + newProtocol = spec.substring(0, index); + break; + } + } + return (newProtocol != null && newProtocol.compareToIgnoreCase("file") == 0); //$NON-NLS-1$ + } + + public void setFileBaseLocation(String newFileBaseLocation) { + fFileBaseLocation = newFileBaseLocation; + } + + public void setProject(IProject newProject) { + fProject = newProject; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ResourceUtil.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ResourceUtil.java new file mode 100644 index 0000000..ab59ecb --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ResourceUtil.java
@@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; + + +/** + * @deprecated - makes assumptions on behalf of the requester + */ +public class ResourceUtil { + + /** + * Obtains the IFile for a model + * + * @param model + * the model to use + * @return the IFile used to create the model, if it came from an IFile, + * null otherwise + */ + public static IFile getFileFor(IStructuredModel model) { + if (model == null) + return null; + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IFile file = null; + IPath location = new Path(model.getBaseLocation()); + // if the path is not a path in the file system and there are at least + // 2 segments, it might be in the workspace + IFile[] files = root.findFilesForLocation(location); + if (files.length > 0) { + file = files[0]; + } + else if (location.segmentCount() > 1) { + // remember, this IFile isn't guaranteed to exist + file = root.getFile(location); + } + return file; + } + + /** + * Obtain IFiles from IStructuredModel (includes linkedResources) + * + * @return the corresponding files in the workspace, or an empty array if + * none + */ + public static IFile[] getFilesFor(IStructuredModel model) { + if (model == null) + return null; + + IFile[] files = null; + IPath location = new Path(model.getBaseLocation()); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + // if the path is not a path in the file system and there are at least + // 2 segments, it might be in the workspace + if (!location.toFile().exists() && location.segmentCount() > 1) { + // remember, this IFile isn't guaranteed to exist + files = new IFile[]{root.getFile(location)}; + } + else { + files = root.findFilesForLocation(location); + } + return files; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ScriptLanguageKeys.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ScriptLanguageKeys.java new file mode 100644 index 0000000..d886ba2 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/ScriptLanguageKeys.java
@@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + +/** + * Contains list of script languages and mime types + */ +public interface ScriptLanguageKeys { + + public static final String JAVA = "java"; //$NON-NLS-1$ + + public static final String[] JAVA_LANGUAGE_KEYS = new String[]{"java"}; //$NON-NLS-1$ + + public static final String JAVASCRIPT = "javascript"; //$NON-NLS-1$ + public static final String[] JAVASCRIPT_LANGUAGE_KEYS = {"javascript", //$NON-NLS-1$ + "ecmascript", //$NON-NLS-1$ + "javascript1.0", //$NON-NLS-1$ + "javascript1.1", //$NON-NLS-1$ + "javascript1.2", //$NON-NLS-1$ + "javascript1.3", //$NON-NLS-1$ + "javascript1.4", //$NON-NLS-1$ + "javascript1.5", //$NON-NLS-1$ + "javascript1.6", //$NON-NLS-1$ + "jscript", //$NON-NLS-1$ + "sashscript"}; //$NON-NLS-1$ + + public static final String[] JAVASCRIPT_MIME_TYPE_KEYS = {"text/javascript", //$NON-NLS-1$ + "application/ecmascript", //$NON-NLS-1$ + "application/javascript", //$NON-NLS-1$ + "application/x-ecmascript", //$NON-NLS-1$ + "application/x-javascript", //$NON-NLS-1$ + "text/ecmascript", //$NON-NLS-1$ + "text/javascript1.0", //$NON-NLS-1$ + "text/javascript1.1", //$NON-NLS-1$ + "text/javascript1.2", //$NON-NLS-1$ + "text/javascript1.3", //$NON-NLS-1$ + "text/javascript1.4", //$NON-NLS-1$ + "text/javascript1.5", //$NON-NLS-1$ + "text/jscript", //$NON-NLS-1$ + "text/livescript", //$NON-NLS-1$ + "text/x-ecmascript", //$NON-NLS-1$ + "text/x-javascript", //$NON-NLS-1$ + "text/sashscript"}; //$NON-NLS-1$ +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Sorter.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Sorter.java new file mode 100644 index 0000000..41cdf33 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Sorter.java
@@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2001, 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + + + +/** + * The SortOperation takes a collection of objects and returns a sorted + * collection of these objects. Concrete instances of this class provide the + * criteria for the sorting of the objects based on the type of the objects. + */ +public abstract class Sorter { + + /** + * Returns true iff elementTwo is 'greater than' elementOne. This is the + * 'ordering' method of the sort operation. Each subclass overrides this + * method with the particular implementation of the 'greater than' concept + * for the objects being sorted. If elementOne and elementTwo are + * equivalent in terms of their sorting order, this method must return + * 'false'. + */ + public abstract boolean compare(Object elementOne, Object elementTwo); + + /** + * Sort the objects in the array and return the array. + */ + private Object[] quickSort(Object[] array, int left, int right) { + int originalLeft = left; + int originalRight = right; + Object mid = array[(left + right) / 2]; + + do { + while (compare(array[left], mid)) + left++; + while (compare(mid, array[right])) + right--; + if (left <= right) { + Object tmp = array[left]; + array[left] = array[right]; + array[right] = tmp; + left++; + right--; + } + } while (left <= right); + + if (originalLeft < right) + array = quickSort(array, originalLeft, right); + if (left < originalRight) + array = quickSort(array, left, originalRight); + + return array; + } + + /** + * Return a new (quick)sorted array from this unsorted array. The original + * array is not modified. + */ + public Object[] sort(Object[] unSortedCollection) { + int size = unSortedCollection.length; + Object[] sortedCollection = new Object[size]; + + //copy the array so can return a new sorted collection + System.arraycopy(unSortedCollection, 0, sortedCollection, 0, size); + if (size > 1) + quickSort(sortedCollection, 0, size - 1); + + return sortedCollection; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/TextUtilities.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/TextUtilities.java new file mode 100644 index 0000000..423fa49 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/TextUtilities.java
@@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + + + +/** + * Collection of text functions. + * @deprecated - the JFace class is public in 3.1 + */ +// This class was originally copied from org.eclipse.jface.text, and made +// public +public class TextUtilities { + /** + * Returns whether the text ends with one of the given search strings. + */ + public static boolean endsWith(String[] searchStrings, String text) { + for (int i = 0; i < searchStrings.length; i++) { + if (text.endsWith(searchStrings[i])) + return true; + } + return false; + } + + /** + * Returns the position in the string greater than offset of the longest + * matching search string. + */ + public static int[] indexOf(String[] searchStrings, String text, int offset) { + + int[] result = {-1, -1}; + + for (int i = 0; i < searchStrings.length; i++) { + int index = text.indexOf(searchStrings[i], offset); + if (index >= 0) { + + if (result[0] == -1) { + result[0] = index; + result[1] = i; + } else if (index < result[0]) { + result[0] = index; + result[1] = i; + } else if (index == result[0] && searchStrings[i].length() > searchStrings[result[1]].length()) { + result[0] = index; + result[1] = i; + } + } + } + + return result; + + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/URIResolver.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/URIResolver.java new file mode 100644 index 0000000..e2259eb --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/URIResolver.java
@@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + +import java.io.InputStream; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; + + +/** + * @deprecated + * + * Should use extensible URIResolver from org.eclipse.wst.common.uriresolver + * instead. + */ + +public interface URIResolver { + + String getFileBaseLocation(); + + /** + * Resolve the (possibly relative) URI acording to RFC1808 using the + * default file base location. Resolves resource references into absolute + * resource locations without ensuring that the resource actually exists. + */ + String getLocationByURI(String uri); + + /** + * Resolve the (possibly relative) URI acording to RFC1808 using the + * default file base location. Resolves resource references into absolute + * resource locations without ensuring that the resource actually exists. + * + * If resolveCrossProjectLinks is set to true, then this method will + * properly resolve the URI if it is a valid URI to another (appropriate) + * project. + */ + String getLocationByURI(String uri, boolean resolveCrossProjectLinks); + + /** + * Perform the getLocationByURI action using the baseReference as the + * point of reference instead of the default for this resolver + */ + String getLocationByURI(String uri, String baseReference); + + /** + * Perform the getLocationByURI action using the baseReference as the + * point of reference instead of the default for this resolver + * + * If resolveCrossProjectLinks is set to true, then this method will + * properly resolve the URI if it is a valid URI to another (appropriate) + * project. + */ + String getLocationByURI(String uri, String baseReference, boolean resolveCrossProjectLinks); + + IProject getProject(); + + IContainer getRootLocation(); + + /** + * Attempts to return a direct inputstream to the given URI which must be + * relative to the default point of reference. + * + */ + InputStream getURIStream(String uri); + + void setFileBaseLocation(String newLocation); + + void setProject(IProject newProject); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Utilities.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Utilities.java new file mode 100644 index 0000000..911c5e9 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/util/Utilities.java
@@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.util; + + + +import java.io.BufferedInputStream; +import java.io.InputStream; + +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; +import org.eclipse.wst.sse.core.internal.encoding.util.BufferedLimitedStream; + + + +public class Utilities { + + /** + * a common calculation in some of the parsing methods (e.g. in + * ContextRegion and IStructuredDocumentRegion) + */ + public static int calculateLengthDifference(String changes, int lengthToReplace) { + // determine the length by the text itself, or, if there is no text to + // insert (i.e. we are doing a delete) then calculate the length as + // a negative number to denote the amount to delete. + // For a straight insert, the selection Length will be zero. + int lengthDifference = 0; + if (changes == null) { + // the delete case + lengthDifference = 0 - lengthToReplace; + } + else { + lengthDifference = changes.length() - lengthToReplace; + } + if (Debug.debugStructuredDocument) { + System.out.println("lengthDifference: " + lengthDifference);//$NON-NLS-1$ + } + return lengthDifference; + } + + /** + * Returns true iff both parameters are not null and the object is within + * the array. Careful, this uses identity. Not good for basic strings. + */ + public static boolean contains(Object[] objectArray, Object object) { + boolean result = false; + // if object or objectArray is null, return false + if ((objectArray != null) && (object != null)) { + for (int i = 0; i < objectArray.length; i++) { + if (objectArray[i] == object) { + result = true; + break; + } + } + } + return result; + } + + public static boolean containsString(String[] objectArray, String object) { + boolean result = false; + // if object or objectArray is null, return false + if ((objectArray != null) && (object != null)) { + for (int i = 0; i < objectArray.length; i++) { + if (objectArray[i].equals(object)) { + result = true; + break; + } + } + } + return result; + } + + /** + * Ensures that an InputStream has mark/reset support, is readlimit is + * set, and that the stream is "limitable" (that is, reports "end of + * input" rather than allow going past mark). This is very specialized + * stream introduced to overcome + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=67211. See also + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=68565 + */ + public static InputStream getLimitedStream(InputStream original) { + if (original == null) + return null; + if (original instanceof BufferedLimitedStream) + return original; + return new BufferedLimitedStream(original, CodedIO.MAX_BUF_SIZE); + } + + /** + * <p> + * Ensures that an InputStream has mark/reset support. + * </p> + * <p> + * It's vital that a BufferedInputStream <b>not</b> be wrapped in another + * BufferedInputStream as each can preemptively consume <i>n</i> bytes + * (e.g. 2048) from the parent stream before any requests are made. The + * cascading effect is that the second/inner BufferedInputStream can never + * rewind itself to the first <i>n</i> bytes since they were already + * consumed by its parent. + * </p> + */ + public static InputStream getMarkSupportedStream(InputStream original) { + if (original == null) + return null; + if (original.markSupported()) + return original; + InputStream buffered = new BufferedInputStream(original, CodedIO.MAX_BUF_SIZE); + buffered.mark(CodedIO.MAX_MARK_SIZE); + return buffered; + } + + /** + * Used for log/trace messages. Id is assumed to be some form of a + * filename. See IModelManager. + */ + public static String makeShortId(Object id) { + if (id == null) + id = "NOID";//$NON-NLS-1$ + String whole = id.toString(); + String part = whole.substring(whole.lastIndexOf("/") + 1); //$NON-NLS-1$ + return "..." + part; //$NON-NLS-1$ + } + + + /** + * Utilities constructor comment. + */ + public Utilities() { + super(); + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ErrorInfo.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ErrorInfo.java new file mode 100644 index 0000000..d36b92c --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ErrorInfo.java
@@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 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.wst.sse.core.internal.validate; + + + +public interface ErrorInfo { + + public String getHint(); + + public int getLength(); + + public int getOffset(); + + public int getState(); + + public short getTargetType(); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationAdapter.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationAdapter.java new file mode 100644 index 0000000..336020e --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationAdapter.java
@@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.validate; + + + +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; + +/** + */ +public interface ValidationAdapter extends INodeAdapter { + + /** + */ + void setReporter(ValidationReporter reporter); + + /** + */ + void validate(IndexedRegion node); +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationMessage.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationMessage.java new file mode 100644 index 0000000..5f2f605 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationMessage.java
@@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.validate; + + + +/** + */ +public class ValidationMessage { + public static final int IGNORE = -1; + public static final int ERROR = 1; + public static final int INFORMATION = 3; + public static final int WARNING = 2; + private int length; + + private String message; + private int offset; + private int severity; + + /** + */ + public ValidationMessage(String message, int offset, int severity) { + this(message, offset, 0, severity); + } + + /** + */ + public ValidationMessage(String message, int offset, int length, int severity) { + super(); + + this.message = message; + this.offset = offset; + this.length = length; + this.severity = severity; + } + + /** + */ + public int getLength() { + return this.length; + } + + /** + */ + public String getMessage() { + return this.message; + } + + /** + */ + public int getOffset() { + return this.offset; + } + + /** + */ + public int getSeverity() { + return this.severity; + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationReporter.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationReporter.java new file mode 100644 index 0000000..82a06b5 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidationReporter.java
@@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.internal.validate; + + + + +/** + */ +public interface ValidationReporter { + + /** + */ + void report(ValidationMessage message); + + void report(ErrorInfo info); + +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidatorGroupListener.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidatorGroupListener.java new file mode 100644 index 0000000..c34c271 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/validate/ValidatorGroupListener.java
@@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2008 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.wst.sse.core.internal.validate; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.validation.IValidatorGroupListener; +import org.eclipse.wst.validation.ValidationState; + +public class ValidatorGroupListener implements IValidatorGroupListener { + + Map fDiagnosticMap = new HashMap(); + private static final boolean _debug = false; + + public ValidatorGroupListener() { + } + + protected void finalize() throws Throwable { + super.finalize(); + if (fDiagnosticMap != null && !fDiagnosticMap.isEmpty()) { + Object[] paths = fDiagnosticMap.keySet().toArray(); + for (int i = 0; i < paths.length; i++) { + Logger.log(Logger.ERROR, "Leaked model: " + paths[i]); + validationFinishing(ResourcesPlugin.getWorkspace().getRoot().getFile((IPath) paths[i]), new NullProgressMonitor(), null); + } + } + } + + public void validationFinishing(IResource resource, IProgressMonitor monitor, ValidationState state) { + if (_debug) + System.out.println("Finishing:" + resource.getFullPath()); + if (resource.getType() != IResource.FILE) + return; + + IStructuredModel model = (IStructuredModel) fDiagnosticMap.remove(resource.getFullPath()); + if (model != null) { + model.releaseFromRead(); + } + } + + public void validationStarting(IResource resource, IProgressMonitor monitor, ValidationState state) { + if (_debug) + System.out.println("Starting: " + resource.getFullPath()); + try { + if (monitor != null && !monitor.isCanceled()) { + if (resource.getType() != IResource.FILE) + return; + + IModelManager modelManager = StructuredModelManager.getModelManager(); + // possible when shutting down + if (modelManager != null) { + IStructuredModel model = modelManager.getModelForRead((IFile) resource); + if (model != null) { + fDiagnosticMap.put(resource.getFullPath(), model); + } + } + } + } + catch (Exception e) { + Logger.logException(e); + } + } +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/text/IStructuredPartitions.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/text/IStructuredPartitions.java new file mode 100644 index 0000000..e1cfc24 --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/text/IStructuredPartitions.java
@@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 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.wst.sse.core.text; + +/** + * This interface is not intended to be implemented. + * It defines the partitioning for StructuredDocuments. + * Clients should reference the partition type Strings defined here directly. + * + * @since 1.1 + */ +public interface IStructuredPartitions { + + String DEFAULT_PARTITION = "org.eclipse.wst.sse.ST_DEFAULT"; //$NON-NLS-1$ + String UNKNOWN_PARTITION = "org.eclipse.wst.sse.UNKNOWN_PARTITION_TYPE"; //$NON-NLS-1$ +}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/utils/StringUtils.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/utils/StringUtils.java new file mode 100644 index 0000000..72dca7d --- /dev/null +++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/utils/StringUtils.java
@@ -0,0 +1,751 @@ +/******************************************************************************* + * Copyright (c) 2001, 2010 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (Intalio) - bug 300430 - String concatenation + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.utils; + + + +import java.util.ArrayList; +import java.util.List; +import com.ibm.icu.util.StringTokenizer; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.sse.core.internal.Logger; + + +public class StringUtils { + protected static final String AMPERSTAND = "&"; //$NON-NLS-1$ + protected static final String AMPERSTAND_ENTITY = "&&;"; //$NON-NLS-1$ + protected static final String CARRIAGE_RETURN = "\r"; //$NON-NLS-1$ + protected static final String CARRIAGE_RETURN_ENTITY = "\\r"; //$NON-NLS-1$ + protected static final String CR = "\r"; //$NON-NLS-1$ + protected static final String CRLF = "\r\n"; //$NON-NLS-1$ + protected static final String DELIMITERS = " \t\n\r\f"; //$NON-NLS-1$ + protected static final String DOUBLE_QUOTE = "\""; //$NON-NLS-1$ + protected static final char DOUBLE_QUOTE_CHAR = '\"'; //$NON-NLS-1$ + protected static final String DOUBLE_QUOTE_ENTITY = """; //$NON-NLS-1$ + + protected static final String EQUAL_SIGN = "="; //$NON-NLS-1$ + protected static final String EQUAL_SIGN_ENTITY = "="; //$NON-NLS-1$ + private static final String FALSE = "false"; //$NON-NLS-1$ + protected static final String GREATER_THAN = ">"; //$NON-NLS-1$ + protected static final String GREATER_THAN_ENTITY = ">"; //$NON-NLS-1$ + protected static final String LESS_THAN = "<"; //$NON-NLS-1$ + protected static final String LESS_THAN_ENTITY = "<"; //$NON-NLS-1$ + protected static final String LF = "\n"; //$NON-NLS-1$ + protected static final String LINE_FEED = "\n"; //$NON-NLS-1$ + protected static final String LINE_FEED_ENTITY = "\\n"; //$NON-NLS-1$ + protected static final String LINE_FEED_TAG = "<dl>"; //$NON-NLS-1$ + protected static final String LINE_TAB = "\t"; //$NON-NLS-1$ + protected static final String LINE_TAB_ENTITY = "\\t"; //$NON-NLS-1$ + protected static final String LINE_TAB_TAG = "<dd>"; //$NON-NLS-1$ + protected static final String SINGLE_QUOTE = "'"; //$NON-NLS-1$ + protected static final char SINGLE_QUOTE_CHAR = '\''; //$NON-NLS-1$ + protected static final String SINGLE_QUOTE_ENTITY = "'"; //$NON-NLS-1$ + protected static final String SPACE = " "; //$NON-NLS-1$ + protected static final String SPACE_ENTITY = " "; //$NON-NLS-1$ + private static final String TRUE = "true"; //$NON-NLS-1$ + + /** + * Append appendString to the end of aString only if aString does not end + * with the insertString. + */ + public static String appendIfNotEndWith(String aString, String appendString) { + if ((aString != null) && (appendString != null)) + if (aString.endsWith(appendString)) + return aString; + else + return aString + appendString; + else + return aString; + } + + /** + * Breaks out space-separated words into an array of words. For example: + * <code>"no comment"</code> into an array <code>a[0]="no"</code> and + * <code>a[1]= "comment"</code>. + * + * @param value + * the string to be converted + * @return the list of words + */ + public static String[] asArray(String value) { + ArrayList list = new ArrayList(); + StringTokenizer stok = new StringTokenizer(value); + while (stok.hasMoreTokens()) { + list.add(stok.nextToken()); + } + String result[] = new String[list.size()]; + list.toArray(result); + return result; + } + + /** + * Breaks out delim-separated words into an array of words. For example: + * <code>"no comment"</code> into an array <code>a[0]="no"</code> and + * <code>a[1]= "comment"</code>. + * + * @param value + * the string to be converted + * @return the list of words + */ + public static String[] asArray(String value, String delim) { + return asArray(value, delim, false); + } + + /** + * Breaks out delim-separated words into an array of words. For example: + * <code>"no comment"</code> into an array <code>a[0]="no"</code> and + * <code>a[1]= "comment"</code>. + * + * @param value + * the string to be converted + * @return the list of words + */ + public static String[] asArray(String value, String delim, boolean returnTokens) { + ArrayList list = new ArrayList(); + StringTokenizer stok = new StringTokenizer(value, delim, returnTokens); + while (stok.hasMoreTokens()) { + list.add(stok.nextToken()); + } + String result[] = new String[list.size()]; + list.toArray(result); + return result; + } + + /** + * Breaks out delim-separated words into an array of words. For example: + * <code>"abc,,def"</code> into an array <code>a[0]="abc"</code>, + * <code>a[1]=null</code>, and <code>a[2]= "def"</code> where "," is + * the delim. + * + * @param value + * the string to be converted + * @return the list of words + */ + public static String[] asFixedArray(String value, String delim) { + String array[] = asArray(value, delim, true); + int arrayLength = array.length; + boolean stringFound = false; + ArrayList list = new ArrayList(); + + for (int i = 0; i < arrayLength; i++) { + String token = array[i]; + if (token.compareTo(delim) == 0) { + if (!stringFound) + list.add(null); + stringFound = false; + } + else { + list.add(token); + stringFound = true; + } + } + // add one more null if last token is the delim + if (!stringFound) + list.add(null); + + String result[] = new String[list.size()]; + list.toArray(result); + return result; + } + + public static String chop(String source) { + return chop(source, "/"); //$NON-NLS-1$ + } + + public static String chop(String source, String delimiter) { + return source.substring(0, source.lastIndexOf(delimiter)); + } + + public static boolean contains(String[] arrayOfStrings, String needle, boolean caseSensitive) { + boolean result = false; + if (needle == null) + return false; + if (arrayOfStrings == null) + return false; + + if (caseSensitive) { + for (int i = 0; i < arrayOfStrings.length; i++) { + if (needle.equals(arrayOfStrings[i])) { + result = true; + break; + } + } + } + else { + for (int i = 0; i < arrayOfStrings.length; i++) { + if (needle.equalsIgnoreCase(arrayOfStrings[i])) { + result = true; + break; + } + } + } + return result; + } + + public static boolean containsLetters(String fullValue) { + + if (fullValue == null || fullValue.length() == 0) + return false; + + char[] chars = fullValue.toCharArray(); + for (int i = 0; i < fullValue.length(); i++) + if (Character.isLetter(chars[i])) + return true; + + return false; + } + + public static boolean containsLineDelimiter(String aString) { + return indexOfLineDelimiter(aString) != -1; + } + + public static String convertLineDelimiters(String allText, String lineDelimiterToUse) { + IDocument tempDoc = new Document(allText); + + if (lineDelimiterToUse == null) + lineDelimiterToUse = System.getProperty("line.separator"); //$NON-NLS-1$ + + String newText = ""; //$NON-NLS-1$ + int lineCount = tempDoc.getNumberOfLines(); + StringBuffer sb = new StringBuffer(newText); + for (int i = 0; i < lineCount; i++) { + try { + org.eclipse.jface.text.IRegion lineInfo = tempDoc.getLineInformation(i); + int lineStartOffset = lineInfo.getOffset(); + int lineLength = lineInfo.getLength(); + int lineEndOffset = lineStartOffset + lineLength; + sb.append(allText.substring(lineStartOffset, lineEndOffset)); + + if ((i < lineCount - 1) && (tempDoc.getLineDelimiter(i) != null)) { + sb.append(lineDelimiterToUse); + } + } + catch (BadLocationException e) { + // log for now, unless we find reason not to + Logger.log(Logger.INFO, e.getMessage()); + } + } + newText = sb.toString(); + + return newText; + } + + /** + * Replaces all instances of special HTML characters with the appropriate + * HTML entity equivalent. WARNING only use this method for strings that + * dont already have HTML-specific items such as tags and entities. + * + * @param String + * content String to convert + * + * @return String the converted string + * @see HTMLPrinter#convertToHTMLContent(String content) + */ + public static String convertToHTMLContent(String content) { + content = replace(content, AMPERSTAND, AMPERSTAND_ENTITY); + content = replace(content, LESS_THAN, LESS_THAN_ENTITY); + content = replace(content, GREATER_THAN, GREATER_THAN_ENTITY); + content = replace(content, LINE_FEED, LINE_FEED_TAG); + content = replace(content, LINE_TAB, LINE_TAB_TAG); + content = replace(content, SINGLE_QUOTE, SINGLE_QUOTE_ENTITY); + content = replace(content, DOUBLE_QUOTE, DOUBLE_QUOTE_ENTITY); + content = replace(content, SPACE + SPACE, SPACE_ENTITY + SPACE_ENTITY); // replacing + // every + // space + // would + // be + // too + // much + return content; + } + + /** + * Converts a string into a form that will not conflict with saving it + * into an INI file + */ + public static String escape(String normalString) { + if (normalString == null) + return null; + StringBuffer escapedBuffer = new StringBuffer(); + StringTokenizer toker = new StringTokenizer(normalString, EQUAL_SIGN + LINE_FEED + CARRIAGE_RETURN + LINE_TAB, true); + String chunk = null; + while (toker.hasMoreTokens()) { + chunk = toker.nextToken(); + if (chunk.equals(EQUAL_SIGN)) { + escapedBuffer.append(EQUAL_SIGN_ENTITY); + } + else if (chunk.equals(LINE_FEED)) { + escapedBuffer.append(LINE_FEED_ENTITY); + } + else if (chunk.equals(CARRIAGE_RETURN)) { + escapedBuffer.append(CARRIAGE_RETURN_ENTITY); + } + else if (chunk.equals(LINE_TAB)) { + escapedBuffer.append(LINE_TAB_ENTITY); + } + else { + escapedBuffer.append(chunk); + } + } + return escapedBuffer.toString(); + } + + /** + * Returns the first line of the given text without a trailing delimiter + * + * @param text + * @return + */ + public static String firstLineOf(String text) { + if (text == null || text.length() < 1) { + return text; + } + IDocument doc = new Document(text); + try { + int lineNumber = doc.getLineOfOffset(0); + IRegion line = doc.getLineInformation(lineNumber); + return doc.get(line.getOffset(), line.getLength()); + } + catch (BadLocationException e) { + // do nothing + } + return text; + } + + public static int indexOfLastLineDelimiter(String aString) { + return indexOfLastLineDelimiter(aString, aString.length()); + } + + public static int indexOfLastLineDelimiter(String aString, int offset) { + int index = -1; + + if (aString != null && aString.length() > 0) { + index = aString.lastIndexOf(CRLF, offset); + if (index == -1) { + index = aString.lastIndexOf(CR, offset); + if (index == -1) + index = aString.lastIndexOf(LF, offset); + } + } + + return index; + } + + public static int indexOfLineDelimiter(String aString) { + return indexOfLineDelimiter(aString, 0); + } + + public static int indexOfLineDelimiter(String aString, int offset) { + int index = -1; + + if (aString != null && aString.length() > 0) { + index = aString.indexOf(CRLF, offset); + if (index == -1) { + index = aString.indexOf(CR, offset); + if (index == -1) + index = aString.indexOf(LF, offset); + } + } + + return index; + } + + public static int indexOfNonblank(String aString) { + return indexOfNonblank(aString, 0); + } + + public static int indexOfNonblank(String aString, int offset) { + int index = -1; + + if (aString != null && aString.length() > 0) { + for (int i = offset; i < aString.length(); i++) { + if (DELIMITERS.indexOf(aString.substring(i, i + 1)) == -1) { + index = i; + break; + } + } + } + + return index; + } + + /** + * Insert insertString to the beginning of aString only if aString does + * not start with the insertString. + */ + public static String insertIfNotStartWith(String aString, String insertString) { + if ((aString != null) && (insertString != null)) + if (aString.startsWith(insertString)) + return aString; + else + return insertString + aString; + else + return aString; + } + + public static boolean isQuoted(String string) { + if ((string == null) || (string.length() < 2)) + return false; + + int lastIndex = string.length() - 1; + char firstChar = string.charAt(0); + char lastChar = string.charAt(lastIndex); + + return (((firstChar == SINGLE_QUOTE_CHAR) && (lastChar == SINGLE_QUOTE_CHAR)) || ((firstChar == DOUBLE_QUOTE_CHAR) && (lastChar == DOUBLE_QUOTE_CHAR))); + } + + /** + * Unit tests. + * + * @param args + * java.lang.String[] + */ + public static void main(String[] args) { + // testPaste(); + testStripNonLetterDigits(); + } + + /* + * Returns the merged form of both strings + */ + public static String merge(String newStart, String newEnd) { + String[] regions = overlapRegions(newStart, newEnd); + return regions[0] + regions[1] + regions[2]; + } + + public static int occurrencesOf(String searchString, char targetChar) { + int result = 0; + int len = searchString.length(); + for (int i = 0; i < len; i++) { + if (targetChar == searchString.charAt(i)) + result++; + } + return result; + } + + /** + * + * @return java.lang.String[] + * @param start + * java.lang.String + * @param end + * java.lang.String + * + * Returns a 3 String array containing unique text from the start, + * duplicated text that overlaps the start and end, and the unique text + * from the end. + */ + private static String[] overlapRegions(String start, String end) { + String[] results = null; + if (start != null && end == null) { + results = new String[]{start, "", ""}; //$NON-NLS-2$//$NON-NLS-1$ + } + else if (start == null && end != null) { + results = new String[]{"", "", end}; //$NON-NLS-2$//$NON-NLS-1$ + } + else if (start == null && end == null) { + results = new String[]{"", "", ""}; //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + } + else if (start != null && end != null) { + + int startLength = start.length(); + int endLength = end.length(); + + if (startLength == 0 || endLength == 0) { + results = new String[]{"", "", ""}; //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + } + else { + results = new String[3]; + String testStart = ""; //$NON-NLS-1$ + String testEnd = ""; //$NON-NLS-1$ + int mergeLength = Math.min(startLength, endLength); + boolean finished = false; + while (mergeLength > 0 && !finished) { + testStart = start.substring(startLength - mergeLength); + testEnd = end.substring(0, mergeLength); + // case sensitive + if (testStart.equals(testEnd)) { + finished = true; + results[0] = start.substring(0, startLength - mergeLength); + results[1] = start.substring(startLength - mergeLength); + results[2] = end.substring(mergeLength); + } + mergeLength--; + } + if (!finished) { + results[0] = start; + results[1] = ""; //$NON-NLS-1$ + results[2] = end; + } + } + } + return results; + } + + /** + * Packs an array of Strings into a single comma delimited String. + * + * @param strings + * @return + * @todo Generated comment + */ + public static String pack(String[] strings) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < strings.length; i++) { + buf.append(StringUtils.replace(strings[i], ",", ",")); //$NON-NLS-1$ //$NON-NLS-2$ + if (i < strings.length - 1) + buf.append(","); //$NON-NLS-1$ + } + return buf.toString(); + } + + /* + * Pastes the new text into the old at the start position, replacing text + * implied by length. + */ + public static String paste(String oldText, String newText, int start, int length) { + String result = null; + StringBuffer sb = new StringBuffer(); + int startIndex = start; + int endIndex = start + length; + if (startIndex > oldText.length()) { + startIndex = oldText.length(); + } + sb.append(oldText.substring(0, startIndex)); + // null or empty new text accompliches a delete + if (newText != null) { + sb.append(newText); + } + if (endIndex < oldText.length()) { + + sb.append(oldText.substring(endIndex)); + } + result = sb.toString(); + return result; + } + + /** + * Replace matching literal portions of a string with another string + */ + public static String replace(String aString, String source, String target) { + if (aString == null) + return null; + String normalString = ""; //$NON-NLS-1$ + int length = aString.length(); + int position = 0; + int previous = 0; + int spacer = source.length(); + StringBuffer sb = new StringBuffer(normalString); + while (position + spacer - 1 < length && aString.indexOf(source, position) > -1) { + position = aString.indexOf(source, previous); + sb.append(normalString); + sb.append(aString.substring(previous, position)); + sb.append(target); + position += spacer; + previous = position; + } + sb.append(aString.substring(position, aString.length())); + normalString = sb.toString(); + + return normalString; + } + + /** + * Restore the entity references for markup delimiters in text where they + * have been replaced by the proper Unicode values through a DOM text + * parser. + */ + public static String restoreMarkers(String text) { + String content = text; + content = replace(content, AMPERSTAND, AMPERSTAND_ENTITY); + content = replace(content, LESS_THAN, LESS_THAN_ENTITY); + content = replace(content, GREATER_THAN, GREATER_THAN_ENTITY); + return content; + } + + /** + * Removes extra whitespace characters and quotes + */ + public static String strip(String quotedString) { + if (quotedString == null || quotedString.length() == 0) + return quotedString; + String trimmed = quotedString.trim(); + if (trimmed.length() < 2) + return quotedString; + + char first = trimmed.charAt(0); + char nextToLast = trimmed.charAt(trimmed.length() - 2); + char last = trimmed.charAt(trimmed.length() - 1); + + if ((first == '\"' && last == '\"' && nextToLast != '\\') || (first == '\'' && last == '\'' && nextToLast != '\\')) { + return trimmed.substring(1, trimmed.length() - 1); + } + return trimmed; + } + + /** + * This method strips anything from the beginning and end of a string that + * is not a letter or digit. It is used by some encoding detectors to come + * up with the encoding name from illformed input (e.g in <?xml + * encoding="abc?> -- where final quote is left off, the '>' is returned + * with the rest of the attribute value 'abc'). + */ + public static String stripNonLetterDigits(String fullValue) { + if (fullValue == null || fullValue.length() == 0) + return fullValue; + int fullValueLength = fullValue.length(); + int firstPos = 0; + while (firstPos < fullValueLength && !Character.isLetterOrDigit(fullValue.charAt(firstPos))) { + firstPos++; + } + int lastPos = fullValueLength - 1; + while (lastPos > firstPos && !Character.isLetterOrDigit(fullValue.charAt(lastPos))) { + lastPos--; + } + String result = fullValue; + if (firstPos != 0 || lastPos != fullValueLength) { + result = fullValue.substring(firstPos, lastPos + 1); + } + return result; + } + + /** + * Similar to strip, except quotes don't need to match such as "UTF' is + * still stripped of both quotes. (Plus, this one does not detect escaped + * quotes) + */ + public static String stripQuotes(String quotedValue) { + if (quotedValue == null) + return null; + // normally will never have leading or trailing blanks, + // but if it does, we'll do lenient interpretation + return stripQuotesLeaveInsideSpace(quotedValue).trim(); + } + + /** + * Like strip quotes, except leaves the start and end space inside the + * quotes + * + * @param quotedValue + * @return + */ + public static String stripQuotesLeaveInsideSpace(String quotedValue) { + if (quotedValue == null) + return null; + // nomally will never have leading or trailing blanks ... but just in + // case. + String result = quotedValue.trim(); + int len = result.length(); + if (len > 0) { + char firstChar = result.charAt(0); + if ((firstChar == SINGLE_QUOTE_CHAR) || (firstChar == DOUBLE_QUOTE_CHAR)) { + result = result.substring(1, len); + } + len = result.length(); + if (len > 0) { + char lastChar = result.charAt(len - 1); + if ((lastChar == SINGLE_QUOTE_CHAR) || (lastChar == DOUBLE_QUOTE_CHAR)) { + result = result.substring(0, len - 1); + } + } + } + return result; + } + + public static void testPaste() { + String testString = "The quick brown fox ..."; //$NON-NLS-1$ + System.out.println(paste(testString, null, 4, 5)); + System.out.println(paste(testString, null, 4, 6)); + System.out.println(paste(testString, "", 4, 6)); //$NON-NLS-1$ + System.out.println(paste(testString, "fast", 4, 6)); //$NON-NLS-1$ + System.out.println(paste(testString, "fast ", 4, 6)); //$NON-NLS-1$ + System.out.println(paste(testString, "But ", 0, 0)); //$NON-NLS-1$ + System.out.println(paste("", "burp", 4, 6)); //$NON-NLS-2$//$NON-NLS-1$ + } + + public static void testStripNonLetterDigits() { + String testString = "abc"; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + testString = ""; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + testString = "\"abc\""; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + testString = "\"ab-c1?"; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + testString = "+++"; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + testString = "abc="; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + testString = "abc "; //$NON-NLS-1$ + System.out.println(testString + " -->" + stripNonLetterDigits(testString) + "<--"); //$NON-NLS-1$ //$NON-NLS-2$ + + } + + public static String toString(boolean booleanValue) { + if (booleanValue) + return TRUE; + else + return FALSE; + } + + /** + * Remove "escaped" chars from a string. + */ + public static String unescape(String aString) { + if (aString == null) + return null; + String normalString = replace(aString, EQUAL_SIGN_ENTITY, EQUAL_SIGN); + normalString = replace(normalString, LINE_FEED_ENTITY, LINE_FEED); + normalString = replace(normalString, CARRIAGE_RETURN_ENTITY, CARRIAGE_RETURN); + normalString = replace(normalString, LINE_TAB_ENTITY, LINE_TAB); + return normalString; + } + + public static String uniqueEndOf(String newStart, String newEnd) { + String[] regions = overlapRegions(newStart, newEnd); + return regions[2]; + } + + /** + * Unpacks a comma delimited String into an array of Strings + * + * @param s + * @return + * @todo Generated comment + */ + public static String[] unpack(String s) { + if (s == null) + return new String[0]; + StringTokenizer toker = new StringTokenizer(s, ","); //$NON-NLS-1$ + List list = new ArrayList(); + while (toker.hasMoreTokens()) { + // since we're separating the values with ',', escape ',' in the + // values + list.add(StringUtils.replace(toker.nextToken(), ",", ",").trim()); //$NON-NLS-1$ //$NON-NLS-2$ + } + return (String[]) list.toArray(new String[0]); + } + + /** + * StringUtils constructor comment. + */ + private StringUtils() { + super(); + } + +}