summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpelder2007-04-04 10:53:54 (EDT)
committerpelder2007-04-04 10:53:54 (EDT)
commit269ac5b36386165367d80aa4d9a8c338d00a8d17 (patch)
treefc7939803e9c389e9a4722794979fb442a187dd4
parentdb34c48fda60762547a129b124fb0a22d7eff1e6 (diff)
downloadorg.eclipse.jet-269ac5b36386165367d80aa4d9a8c338d00a8d17.zip
org.eclipse.jet-269ac5b36386165367d80aa4d9a8c338d00a8d17.tar.gz
org.eclipse.jet-269ac5b36386165367d80aa4d9a8c338d00a8d17.tar.bz2
[180962,131208] Create org.eclipse.jet.core, add JET1 compilation
-rw-r--r--plugins/org.eclipse.jet.core/.classpath7
-rw-r--r--plugins/org.eclipse.jet.core/.cvsignore1
-rw-r--r--plugins/org.eclipse.jet.core/.project28
-rw-r--r--plugins/org.eclipse.jet.core/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.core.prefs12
-rw-r--r--plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.ui.prefs4
-rw-r--r--plugins/org.eclipse.jet.core/META-INF/MANIFEST.MF20
-rw-r--r--plugins/org.eclipse.jet.core/about.html86
-rw-r--r--plugins/org.eclipse.jet.core/build.properties6
-rw-r--r--plugins/org.eclipse.jet.core/plugin.properties19
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/AbstractContextExtender.java64
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/BufferedJET2Writer.java76
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/ContextLogEntry.java304
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/IWriterListener.java50
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Context.java504
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Template.java35
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoader.java36
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoaderExtension.java38
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Writer.java194
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/compiler/JETCompilerOptions.java253
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/AbstractTemplateResolver.java166
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/DefaultTemplateResolver.java173
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IJETParser.java31
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IProblem.java136
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITagLibraryResolver.java27
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateInput.java46
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolver.java46
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelper.java44
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelperFactory.java33
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ProblemSeverity.java55
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/RecursiveIncludeException.java84
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/TemplateInputException.java54
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElement.java42
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElements.java119
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Comment.java85
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/IncludedContent.java140
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETAST.java231
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTElement.java199
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTParser.java182
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTVisitor.java271
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETCompilationUnit.java290
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETDirective.java90
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaDeclaration.java54
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaElement.java77
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaExpression.java55
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaScriptlet.java63
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Problem.java258
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TagLibraryUsageManager.java213
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TextElement.java142
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElement.java142
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElementEnd.java82
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLElement.java124
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLEmptyElement.java59
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/NewLineUtil.java54
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/DuplicateGeneratedClassException.java75
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/ICompilerOutput.java49
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IJETCompiler.java123
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependencies.java109
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependenciesUtil.java55
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/UniqueNameGenerator.java221
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateInput.java68
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateResolverHelper.java92
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/ElementStack.java137
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/IncludeAlternativesTracker.java181
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET1Parser.java598
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET2Parser.java649
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/LineInfo.java150
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TagValidationVisitor.java172
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TextTrimmingVisitor.java181
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/CommentElementDelegate.java77
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/DeclarationElementDelegate.java63
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/ErrorRedirectingCoreElementDelegate.java56
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETCoreElement.java30
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETException.java41
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETMark.java268
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener.java39
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener2.java40
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParser.java643
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETReader.java832
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/Messages.java22
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/MessagesUtil.java62
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/XMLElementDelegate.java168
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/messages.properties21
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.java100
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.properties48
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/taglib/TagLibraryReferenceImpl.java71
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/CustomTagKind.java69
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/JET2TagException.java70
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/RuntimeTagElement.java96
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagAttributeDefinition.java76
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagDefinition.java121
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagFactory.java33
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagInfo.java152
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibrary.java71
-rw-r--r--plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibraryReference.java48
95 files changed, 11784 insertions, 0 deletions
diff --git a/plugins/org.eclipse.jet.core/.classpath b/plugins/org.eclipse.jet.core/.classpath
new file mode 100644
index 0000000..ce73933
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.jet.core/.cvsignore b/plugins/org.eclipse.jet.core/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/plugins/org.eclipse.jet.core/.project b/plugins/org.eclipse.jet.core/.project
new file mode 100644
index 0000000..6e125fd
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jet.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/plugins/org.eclipse.jet.core/.settings/org.eclipse.core.resources.prefs b/plugins/org.eclipse.jet.core/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..4fb1938
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Apr 03 17:01:14 EDT 2007
+eclipse.preferences.version=1
+encoding//src/org/eclipse/jet/internal/core/parser/jasper/messages.properties=8859_1
diff --git a/plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..68a96d9
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Tue Apr 03 15:25:19 EDT 2007
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+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.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
diff --git a/plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.ui.prefs b/plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..bb183e1
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,4 @@
+#Tue Apr 03 15:15:00 EDT 2007
+eclipse.preferences.version=1
+internal.default.compliance=user
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return the ${bare_field_name}\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} the ${bare_field_name} to set\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n * \r\n * Contributors\:\r\n * IBM Corporation - initial API and implementation\r\n */</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
diff --git a/plugins/org.eclipse.jet.core/META-INF/MANIFEST.MF b/plugins/org.eclipse.jet.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5d30b65
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jet.core
+Bundle-Version: 1.0.0.qualifier
+Bundle-Localization: plugin
+Eclipse-LazyStart: true
+Export-Package: org.eclipse.jet,
+ org.eclipse.jet.core.compiler,
+ org.eclipse.jet.core.parser,
+ org.eclipse.jet.core.parser.ast,
+ org.eclipse.jet.internal.core;x-friends:="org.eclipse.jet,org.eclipse.jet.tests,org.eclipse.jet.tests.core",
+ org.eclipse.jet.internal.core.compiler;x-friends:="org.eclipse.jet,org.eclipse.jet.tests,org.eclipse.jet.tests.core",
+ org.eclipse.jet.internal.core.parser;x-friends:="org.eclipse.jet.tests.tools,org.eclipse.jet,org.eclipse.jet.tests.core",
+ org.eclipse.jet.internal.core.parser.jasper;x-friends:="org.eclipse.jet,org.eclipse.jet.tests,org.eclipse.jet.tests.core",
+ org.eclipse.jet.internal.taglib,
+ org.eclipse.jet.taglib
+Require-Bundle: org.eclipse.text;bundle-version="[3.2.0,4.0.0)"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Bundle-Vendor: %providerName
diff --git a/plugins/org.eclipse.jet.core/about.html b/plugins/org.eclipse.jet.core/about.html
new file mode 100644
index 0000000..dc8cba8
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/about.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>May 2, 2006</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; 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 (&quot;Redistributor&quot;) 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</a>.</p>
+
+
+ <h3>Third Party Content</h3>
+ <p>The Content includes items that have been sourced from third parties as set out below. If you
+ did not receive this Content directly from the Eclipse Foundation, the following is provided
+ for informational purposes only, and you should look to the Redistributor's license for
+ terms and conditions of use.</p>
+
+<p><b>Tomcat 3.2.4</b></p>
+<p>The plug-in is based on software developed by The Apache Software Foundation as part of the Jakarta project.</p>
+
+<p>The content in the org.eclipse.jet.internal.tools.parser.jasper package (&quot;JET&quot;) is based on code in the org.apache.jasper.compiler package of Tomcat.</p>
+
+<p>The JET binary code can be found in the plug-in JAR in the org\eclipse\jet\internal\tools\parser\jasper directory.</p>
+
+<p>The JET source code can be found in src.zip in the the org\eclipse\jet\internal\tools\parser\jasper directory.</p>
+
+<p>JET is:</p>
+
+<blockquote>Copyright (c) 1999 The Apache Software Foundation. All rights reserved.</blockquote>
+
+<p>Your use of JET is subject to the terms and conditions of the
+<a href="http://jakarta.apache.org/ant/manual/LICENSE">Apache Software License 1.1</a>.
+More specifically:</p>
+<blockquote>
+ 1. Redistributions of source code must retain the above copyright notice, this list of<br />
+ conditions and the following disclaimer.<br />
+<br />
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of<br />
+ conditions and the following disclaimer in the documentation and/or other materials<br />
+ provided with the distribution.<br />
+<br />
+ 3. The end-user documentation included with the redistribution, if any, must include the<br />
+ following acknowledgment:<br />
+ <blockquote>
+ &quot;This product includes software developed by the Apache Software Foundation<br />
+ (<a href="http://www.apache.org/">http://www.apache.org/</a>).&quot;
+ </blockquote>
+ Alternately, this acknowledgment may appear in the software itself, if and wherever such<br />
+ third-party acknowledgments normally appear.<br />
+<br />
+ 4. The names &quot;The Jarkara Project&quot;, &quot;Tomcat&quot; and &quot;Apache Software Foundation&quot; must not be used to endorse or<br />
+ promote products derived from this software without prior written permission. For written<br />
+ permission, please contact <a href="mailto:apache@apache.org">apache@apache.org</a>.<br />
+<br />
+ 5. Products derived from this software may not be called &quot;Apache&quot;, nor may &quot;Apache&quot; appear<br />
+ in their name, without prior written permission of the Apache Software Foundation.<br />
+<br />
+ THIS SOFTWARE IS PROVIDED &quot;AS IS&quot; AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT<br />
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR<br />
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS<br />
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<br />
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br />
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON<br />
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING<br />
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF<br />
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+</blockquote>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/build.properties b/plugins/org.eclipse.jet.core/build.properties
new file mode 100644
index 0000000..b67aba1
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ about.html
diff --git a/plugins/org.eclipse.jet.core/plugin.properties b/plugins/org.eclipse.jet.core/plugin.properties
new file mode 100644
index 0000000..fac2312
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/plugin.properties
@@ -0,0 +1,19 @@
+# <copyright>
+# </copyright>
+#
+# $Id: plugin.properties,v 1.1 2007/04/04 14:53:54 pelder Exp $
+
+# ====================================================================
+# To code developer:
+# Do NOT change the properties between this line and the
+# "%%% END OF TRANSLATED PROPERTIES %%%" line.
+# Make a new property name, append to the end of the file and change
+# the code to use the new property.
+# ====================================================================
+
+# ====================================================================
+# %%% END OF TRANSLATED PROPERTIES %%%
+# ====================================================================
+
+pluginName = Eclipse Modeling Framework Technologies - JET Core
+providerName = Eclipse.org
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/AbstractContextExtender.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/AbstractContextExtender.java
new file mode 100644
index 0000000..1f2465b
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/AbstractContextExtender.java
@@ -0,0 +1,64 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: AbstractContextExtender.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet;
+
+
+/**
+ * A base class for extending the JET2 context.
+ *
+ */
+public abstract class AbstractContextExtender
+{
+
+ private final JET2Context baseContext;
+
+ /**
+ *
+ */
+ protected AbstractContextExtender(JET2Context context)
+ {
+ super();
+ this.baseContext = context;
+ if (!context.hasContextExtender(this.getClass()))
+ {
+ context.registerContextExtender(this.getClass(), createExtendedData(context));
+ }
+ }
+
+ /**
+ * Called by the AbstractContextExtender constructor if the extender's data
+ * has not yet been created in the context.
+ * @param context the context in which the data will be created.
+ * @return the extension data object.
+ */
+ protected abstract Object createExtendedData(JET2Context context);
+
+ protected Object getExtendedData()
+ {
+ return baseContext.getContextExtenderData(this.getClass());
+ }
+
+ /**
+ * Return the JET2Context that this extender instance is extending.
+ * @return the hosting context.
+ */
+ public final JET2Context getContext()
+ {
+ return baseContext;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/BufferedJET2Writer.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/BufferedJET2Writer.java
new file mode 100644
index 0000000..45d2f7b
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/BufferedJET2Writer.java
@@ -0,0 +1,76 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: BufferedJET2Writer.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet;
+
+
+/**
+ * Protocol defining a buffered writer for JET. A buffered writer does not
+ * write directly to an output source. Instead, it writes to an internal buffer
+ * that may be later retrieved and modified.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ * @since 0.8.0
+ */
+public interface BufferedJET2Writer extends JET2Writer
+{
+ /**
+ * Return the length of the buffered content.
+ * @return the length
+ */
+ public abstract int getContentLength();
+
+ /**
+ * Return the buffered content of the writer as a string.
+ * @return the content
+ */
+ public abstract String getContent();
+
+ /**
+ * Return a ranged of text within the buffered cotnent of the writer.
+ * @param offset the offset of the text to return
+ * @param length the length of the text to return
+ * @return the content
+ * @throws IllegalArgumentException if offset or length do not fail within
+ * the current contents
+ */
+ public abstract String getContent(int offset, int length);
+
+ /**
+ * Replace content in the buffer.
+ * @param offset the offset of the text to replace
+ * @param length the length of the text to replace
+ * @param text the replacement text
+ * @throws IllegalArgumentException if offset or length do not fall within
+ * the current contents
+ */
+ public abstract void replaceContent(int offset, int length, String text);
+
+ /**
+ * Set the buffer content, removing any previously written content.
+ * @param content
+ */
+ public abstract void setContent(String content);
+
+ /**
+ * Adapt the writer to the given class. The primary use of this method
+ * is to access the underlying buffer implementation.
+ * @param adapterClass the class to return
+ * @return the adapter or <code>null</code> if adapterClass is not supported.
+ */
+ public abstract Object getAdapter(Class adapterClass);
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/ContextLogEntry.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/ContextLogEntry.java
new file mode 100644
index 0000000..5e7b8b8
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/ContextLogEntry.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 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.jet;
+import org.eclipse.jet.taglib.TagInfo;
+
+/**
+ * Entry in the log created by JET2Context log methods
+ *
+ */
+public final class ContextLogEntry {
+
+ /**
+ * Builder for {@link ContextLogEntry} entities
+ *
+ */
+ public static final class Builder {
+
+ private final int severity;
+ private final ContextLogEntry[] children;
+ private String message;
+ private Throwable exception;
+ private String templatePath;
+ private TagInfo tagInfo;
+ private int line;
+ private int col;
+
+ /**
+ * Create a ContextLogEntry Builder that summarizes a collection
+ * of other ContextLog Entries.
+ * @param children a non-null list of ContextLogEntries
+ * @throws NullPointerException if children is <code>null</code> or an array entry is <code>null</code>
+ */
+ public Builder(ContextLogEntry[] children) {
+ if(children == null) {
+ throw new NullPointerException();
+ }
+ this.children = children;
+ int severity = OK;
+ for (int i = 0; i < children.length; i++) {
+ if(children[i] == null) {
+ throw new NullPointerException(String.valueOf(i));
+ }
+ severity = Math.max(severity, children[i].getSeverity());
+ }
+ this.severity = severity;
+ }
+
+ /**
+ * Create a ContextLogEntry Builder for a single entry with the indicated severity
+ * @param severity one of {@link ContextLogEntry#OK}, {@link ContextLogEntry#INFO},
+ * {@link ContextLogEntry#WARNING}, {@link ContextLogEntry#ERROR} or {@link ContextLogEntry#CANCEL}
+ * @throws IllegalArgumentException if severity is not a valid value
+ */
+ public Builder(int severity) {
+ if(severity != ContextLogEntry.OK
+ && severity != ContextLogEntry.INFO
+ && severity != ContextLogEntry.WARNING
+ && severity != ContextLogEntry.ERROR
+ && severity != ContextLogEntry.CANCEL) {
+ throw new IllegalArgumentException();
+ }
+ this.severity = severity;
+ this.children = EMPTY_ENTRIES_ARRAY;
+ }
+
+ /**
+ * Specify an option message for the ContextLogEntry. Note that
+ * this replaces any previously specified or calculated message
+ * for the entry
+ * @param message a message, possibly <code>null</code>
+ * @return this builder
+ */
+ public Builder message(String message) {
+ this.message = message;
+ return this;
+ }
+
+ /**
+ * Specify an exception for the ContextLogEntry.
+ * Note that
+ * this replaces any previously specified exception for the entry
+ * @param exception an exception, possibly <code>null</code>
+ * @return this builder
+ */
+ public Builder exception(Throwable exception) {
+ this.exception = exception;
+ if(message == null) {
+ message(exception.getLocalizedMessage());
+ }
+ if(message == null) {
+ message(exception.toString());
+ }
+ return this;
+ }
+
+ /**
+ * Specify the template path where the entry originates
+ * Note that
+ * this replaces any previously specified templatePath for the entry
+ * @param templatePath a template path
+ * @return this builder
+ */
+ public Builder templatePath(String templatePath) {
+ this.templatePath = templatePath;
+ return this;
+ }
+
+ /**
+ * Specify the template line and column for the entry.
+ * Note that
+ * this replaces any previously specified line and column for the entry
+ * @param line the line
+ * @param col the column
+ * @return this builder
+ */
+ public Builder location(int line, int col) {
+ this.line = line;
+ this.col = col;
+ return this;
+ }
+
+ /**
+ * Specify the JET tag information for this entry.
+ * Note that seting tag information will replace any
+ * previously specified location information.
+ * @param tagInfo a tag information object
+ * @return this builder
+ * @throws NullPointerException if tagInfo is null
+ */
+ public Builder tagInfo(TagInfo tagInfo) {
+ if(tagInfo == null) {
+ throw new NullPointerException();
+ }
+ this.tagInfo = tagInfo;
+ location(tagInfo.getLine(), tagInfo.getCol());
+ return this;
+ }
+
+ /**
+ * Construct the ContextLogEntry based on information provided in the builder.
+ * @return the contenxt log entry
+ */
+ public ContextLogEntry build() {
+ return new ContextLogEntry(this);
+ }
+
+ /**
+ * Returnt the severity for the log entry
+ * @return one of {@link #OK}, {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #CANCEL}.
+ */
+ public int getSeverity() {
+ return severity;
+ }
+ }
+
+ private static final ContextLogEntry[] EMPTY_ENTRIES_ARRAY = new ContextLogEntry[0];
+
+ /**
+ * The bit mask value <code>0x0</code> for a
+ * {@link #getSeverity severity} indicating everything is okay.
+ */
+ public static final int OK = 0x0;
+
+ /**
+ * The bit mask value <code>0x1</code> for a {@link #getSeverity severity}
+ * indicating there is an informational message.
+ */
+ public static final int INFO = 0x1;
+
+ /**
+ * The bit mask value <code>0x2</code> for a {@link #getSeverity severity}
+ * indicating there is warning message.
+ */
+ public static final int WARNING = 0x2;
+
+ /**
+ * The bit mask value <code>0x1</code> for a {@link #getSeverity severity}
+ * indicating there is an error message.
+ */
+ public static final int ERROR = 0x4;
+
+ /**
+ * The bit mask value <code>0x1</code> for a {@link #getSeverity severity}
+ * indicating that the diagnosis was canceled.
+ */
+ public static final int CANCEL = 0x8;
+
+ private final int severity;
+
+ private final ContextLogEntry[] children;
+
+ private final String message;
+
+ private final Throwable exception;
+
+ private final String templatePath;
+
+ private final int line;
+
+ private final int col;
+
+ private final TagInfo tagInfo;
+
+ /**
+ * Construct an entry from the builder
+ * @param builder
+ */
+ private ContextLogEntry(Builder builder) {
+ // TODO Auto-generated constructor stub
+ this.severity = builder.severity;
+
+ if(builder.children.length > 0) {
+ this.children = new ContextLogEntry[builder.children.length];
+ System.arraycopy(builder.children, 0, this.children, 0, builder.children.length);
+ } else {
+ children = EMPTY_ENTRIES_ARRAY;
+ }
+
+ this.message = builder.message == null ? "" : builder.message; //$NON-NLS-1$
+
+ this.exception = builder.exception;
+
+ this.templatePath = builder.templatePath;
+
+ this.line = builder.line;
+ this.col = builder.col;
+ this.tagInfo = builder.tagInfo;
+ }
+
+ /**
+ * Return the array of child log entries
+ * @return the possibly empty array of child log entries
+ */
+ public ContextLogEntry[] getChildren() {
+ ContextLogEntry[] copy = new ContextLogEntry[children.length];
+ System.arraycopy(children, 0, copy, 0, children.length);
+
+ return copy;
+ }
+
+ /**
+ * Return the severity of the entry
+ * @return one of {@link #OK}, {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #CANCEL}.
+ */
+ public int getSeverity() {
+ return severity;
+ }
+
+ /**
+ * Return the log entry message
+ * @return the message a possibly empty (but non-null) string
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Return the exception associated with the log entry
+ * @return the exception. May be <code>null</code>
+ */
+ public Throwable getException() {
+ return exception;
+ }
+
+ /**
+ * Return the template path associated with the log entry
+ * @return the templatePath. May be <code>null</code>.
+ */
+ public String getTemplatePath() {
+ return templatePath;
+ }
+
+ /**
+ * Return the column number associated with the entry
+ * @return the col the column number. A zero or negative value means no column number is associated
+ */
+ public int getCol() {
+ return col;
+ }
+
+ /**
+ * Return the line number associated with the entry
+ * @return the line number. A zero or negative value means no line number is associated
+ */
+ public int getLine() {
+ return line;
+ }
+
+ /**
+ * Return the tag information associated with the entry
+ * @return the tagInfo. May be <code>null</code>.
+ */
+ public TagInfo getTagInfo() {
+ return tagInfo;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/IWriterListener.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/IWriterListener.java
new file mode 100644
index 0000000..6fce45b
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/IWriterListener.java
@@ -0,0 +1,50 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: IWriterListener.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet;
+
+import org.eclipse.jet.taglib.JET2TagException;
+
+
+/**
+ * Callback interface allowing participation in the finalization of a {@link JET2Writer}'s content.
+ *
+ */
+public interface IWriterListener
+{
+
+ /**
+ * Perform any finalization of the content in the writer.
+ * @param writer the writer in the process of being finalized
+ * @param file a handle to object to which the content will ultimately be written. The standard
+ * JET2 Workspace tags pass an org.eclipse.core.resources.IFile, but other tags may pass objects
+ * of other types.
+ * @throws JET2TagException if the method cannot complete normally
+ */
+ public abstract void finalizeContent(JET2Writer writer, Object file) throws JET2TagException;
+
+ /**
+ * Perform any post processing on the committed file based on content written.
+ * @param writer the writer that provided the committed content.
+ * @param file a handle to the object containing the comitted content. The standard
+ * JET2 Workspace tags pass an org.eclipse.core.resources.IFile, but other tags may pass objects
+ * of other types.
+ * @throws JET2TagException if method cannot complete normally.
+ */
+ public abstract void postCommitContent(JET2Writer writer, Object file) throws JET2TagException;
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Context.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Context.java
new file mode 100644
index 0000000..03ed58a
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Context.java
@@ -0,0 +1,504 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2Context.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet;
+
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+import org.eclipse.jet.internal.l10n.JET2Messages;
+import org.eclipse.jet.taglib.JET2TagException;
+import org.eclipse.jet.taglib.TagFactory;
+import org.eclipse.jet.taglib.TagInfo;
+
+
+/**
+ * Define the execution context for a JET2 transform or template.
+ *
+ */
+public final class JET2Context
+{
+
+ /**
+ * Protocol for a listener to the {@link JET2Context} log.
+ */
+ public interface LogListener
+ {
+ public abstract void log(ContextLogEntry entry);
+ }
+
+ private Object source;
+
+ private final List logEntries = new ArrayList();
+
+ /**
+ * Use a LinkedHashSet to ensure listener uniqueness
+ * and to preserve order of addition
+ */
+ private final Set logListeners = new LinkedHashSet();
+
+ private final Map extendedContextData = new HashMap();
+
+ private TagFactory tagFactory = null;
+
+ private final Map globalVariables = new HashMap();
+
+ private String templatePath = ""; //$NON-NLS-1$
+
+ private String jetBundleId;
+
+ /**
+ * Create a JET2 context with the specified source argument and the specified variables.
+ * @param source the source object
+ * @param variables A map <String,Object> of variable names to the object values.
+ */
+ public JET2Context(Object source, Map variables)
+ {
+ this.source = source;
+ globalVariables.putAll(variables);
+ }
+
+ /**
+ * Create a JET2 context with the specified source argument and no variables.
+ * <p>
+ * This is exactly equivalent to:
+ * <pre>
+ * JET2Context(source, Collections.EMPTY_MAP)
+ * </pre>
+ * @param source the source object
+ */
+ public JET2Context(Object source)
+ {
+ this(source, Collections.EMPTY_MAP);
+ }
+
+ /**
+ * Set the source object for the transformation
+ *
+ * @param source
+ */
+ public void setSource(Object source)
+ {
+ this.source = source;
+ }
+
+ /**
+ * Return the source object for the transformation.
+ *
+ * @return the source object
+ */
+ public Object getSource()
+ {
+ return source;
+ }
+
+ /**
+ * @param severity
+ * @param templatePath TODO
+ * @param tagInfo
+ * @param throwable
+ */
+ private void log(int severity, String templatePath, TagInfo tagInfo, String message, Throwable throwable)
+ {
+ final ContextLogEntry.Builder builder = new ContextLogEntry.Builder(severity);
+ if(templatePath != null) {
+ builder.templatePath(templatePath);
+ }
+ if(tagInfo != null) {
+ builder.tagInfo(tagInfo);
+ }
+ if(message != null) {
+ builder.message(message);
+ }
+ if(throwable != null) {
+ builder.exception(throwable);
+ }
+ final ContextLogEntry logEntry = builder.build();
+
+ logEntries.add(logEntry);
+
+ for (Iterator i = logListeners.iterator(); i.hasNext();)
+ {
+ LogListener listener = (LogListener)i.next();
+ listener.log(logEntry);
+
+ }
+ }
+
+ /**
+ * Add a listener to context logging entries.
+ * Adding the same listener more than once has
+ * no effect.
+ * @param listener a log listener
+ */
+ public void addLogListener(LogListener listener) {
+ logListeners.add(listener);
+ }
+
+ /**
+ * Remove a previously registerd listener from the context log.
+ * Attempting to remove an listener not previously registered
+ * with {@link #addLogListener(org.eclipse.jet.JET2Context.LogListener)}
+ * has no effect.
+ * @param listener a log listener
+ */
+ public void removeLogListener(LogListener listener) {
+ logListeners.remove(listener);
+ }
+
+ /**
+ * Return the id of the JET Bundle defining the current template. Used in generating
+ * error messages.
+ * @return the JET Bundle id, or <code>null</code> if not defined.
+ * @see #setJETBundleId(String)
+ */
+ public String getJETBundleId()
+ {
+ return jetBundleId;
+ }
+
+ /**
+ * Set the id of the JET Bundle defining the current template. Used in generating
+ * error messages. If not set, then the ID of the JET plugin is used.
+ * @param jetBundleId the JET Bundle ide.
+ */
+ public void setJETBundleId(String jetBundleId)
+ {
+ this.jetBundleId = jetBundleId;
+
+ }
+
+ // private void log(ExecutionLogEntry entry) {
+ // executionLog.add(entry);
+ // }
+
+ /**
+ * Log an informational message
+ *
+ * @param message
+ */
+ // Used once: LogTag.doFunction()
+ public void logInfo(String message)
+ {
+ log(ContextLogEntry.INFO, getTemplatePath(), null, message, (Throwable)null);
+ }
+
+ /**
+ * Return the path for the executing template. This is used in creating error messages.
+ * @return the template path or <code>null</code> if no templatePath is defined.
+ * @see #setTemplatePath(String)
+ */
+ public String getTemplatePath()
+ {
+ return templatePath;
+ }
+
+ /**
+ * Set the templatePath. The templatePath is used in generating error messages.
+ * @param templatePath the template path or <code>null</code> to indicate no executing template.
+ */
+ public void setTemplatePath(String templatePath)
+ {
+ this.templatePath = templatePath;
+ }
+ /**
+ * Log a warning message
+ *
+ * @param message
+ */
+ // Used once: LogTag.doFunction()
+ public void logWarning(String message)
+ {
+ log(ContextLogEntry.WARNING, getTemplatePath(), null, message, (Throwable)null);
+ }
+
+ /**
+ * Log an error message
+ *
+ * @param message
+ */
+ // Used once: LogTag.doFunction()
+ public void logError(String message)
+ {
+ log(ContextLogEntry.ERROR, getTemplatePath(), null, message, null);
+ }
+
+ /**
+ * Log an exeception that occurred during execution
+ *
+ * @param e
+ */
+ // Used once: TransformContextExtender.commit()
+ public void logError(Throwable e)
+ {
+ log(ContextLogEntry.ERROR, getTemplatePath(), null, null, e);
+ }
+
+ /**
+ * Log an exception that occured during execution, along with a message.
+ *
+ * @param message
+ * @param e
+ * @deprecated Please don't use, will be removed...
+ */
+ // Never used!
+ public void logError(String message, Throwable e)
+ {
+ log(ContextLogEntry.ERROR, getTemplatePath(), null, message, e);
+ }
+
+ public ContextLogEntry getLogEntries() {
+ final ContextLogEntry[] entries = (ContextLogEntry[])logEntries.toArray(new ContextLogEntry[0]);
+ final ContextLogEntry.Builder builder = new ContextLogEntry.Builder(entries);
+
+ switch(builder.getSeverity()) {
+ case ContextLogEntry.OK:
+ builder.message(JET2Messages.JET2Context_SuccessfulExecution);
+ break;
+ case ContextLogEntry.INFO:
+ builder.message(JET2Messages.JET2Context_SuccessfulWithMessages);
+ break;
+ case ContextLogEntry.WARNING:
+ builder.message(JET2Messages.JET2Context_SuccessfulWithWarnings);
+ break;
+ case ContextLogEntry.ERROR:
+ builder.message(JET2Messages.JET2Context_ErrorsInExecution);
+ break;
+ case ContextLogEntry.CANCEL:
+ builder.message(JET2Messages.JET2Context_ExecutionCancelled);
+ break;
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Log an error from the specified tag.
+ * @param tagInfo
+ * @param message the error message to display, or <code>null</code>
+ * @param exception
+ */
+ // Used 3 times: TagSafeRunnable.handleException() x 2, tagFactoryImpl.createTagElement(),
+ public void logError(TagInfo tagInfo, String message, Throwable exception)
+ {
+ log(ContextLogEntry.ERROR, getTemplatePath(), tagInfo, message, exception);
+ }
+
+ private String getContextExtenderId(Class clazz)
+ {
+ return clazz.getName();
+ }
+
+ /**
+ * Test whether the context has an extender of the pass class.
+ * @param extenderClass the extender class
+ * @return <code>true</code> if the context has a registered extender of the passed class.
+ */
+ public boolean hasContextExtender(Class extenderClass)
+ {
+ return extendedContextData.containsKey(getContextExtenderId(extenderClass));
+ }
+
+ /**
+ * Register a context extender class and its data.
+ * <P>
+ * This method is not normally called by clients.
+ * It is intended for use by {@link AbstractContextExtender#AbstractContextExtender(JET2Context)}.
+ * </P>
+ * @param extenderClass the extender class
+ * @param extenderData the data to be associated with the class
+ * @throws IllegalStateException if <code>extenderClass</code> has already been registered on this context.
+ */
+ void registerContextExtender(Class extenderClass, Object extenderData)
+ {
+ String extenderId = getContextExtenderId(extenderClass);
+ if (extendedContextData.containsKey(extenderClass))
+ {
+ throw new IllegalStateException(extenderId + "already registered"); //$NON-NLS-1$
+ }
+
+ extendedContextData.put(extenderId, extenderData);
+ }
+
+ /**
+ * Return the context extension data for the passed class, or null if the extender class
+ * has no associated data, or if <code>extenderClass</code> is not registered on the context.
+ * <P>
+ * This method is not normally called by clients.
+ * It is intended for use by {@link AbstractContextExtender#getExtendedData()}.
+ * </P>
+ * @param extenderClass the context extender class.
+ * @return the associated data or <code>null</code>.
+ */
+ Object getContextExtenderData(Class extenderClass)
+ {
+ return extendedContextData.get(getContextExtenderId(extenderClass));
+ }
+
+ /**
+ * Log an error on the pass template
+ * @param templatePath
+ * @param tagInfo
+ * @param message
+ * @param e
+ */
+ public void logError(String templatePath, TagInfo tagInfo, String message, Throwable e)
+ {
+ log(ContextLogEntry.ERROR, templatePath, tagInfo, message, e);
+
+ }
+
+ private static final Pattern validVariableNamePattern = Pattern.compile("(?:_|\\p{L})(?:_|-|\\.|\\p{L}|\\d)*"); //$NON-NLS-1$
+ /**
+ * Assigne or create a variable, and set its value.
+ * @param var the variable name. Cannot be <code>null</code>.
+ * @param value the variable value.
+ */
+ public void setVariable(String var, Object value) throws JET2TagException
+ {
+ if(!validVariableNamePattern.matcher(var).matches())
+ {
+ throw new JET2TagException(MessageFormat.format(JET2Messages.JET2Context_InvalidVariableName, new Object[] {var}));
+ }
+ globalVariables.put(var, value);
+ }
+
+ /**
+ * Return the value of a context variable
+ * @param var the variable name
+ * @return the value of the variable
+ * @throws JET2TagException if the variable does not exist.
+ */
+ public Object getVariable(String var) throws JET2TagException
+ {
+ if (!hasVariable(var))
+ {
+ String msg = JET2Messages.JET2Context_VariableNotFound;
+ throw new JET2TagException(MessageFormat.format(msg, new Object []{ var }));
+ }
+ return globalVariables.get(var);
+ }
+
+ /**
+ * Return a map of all variables currently defined in the context. The map is a copy
+ * of the variables maintained by the context; changes to the map have no affect on
+ * the context.
+ * @return a Map of variables, where the key is a variable name, and the value is the variable value.
+ */
+ public Map getVariables()
+ {
+ return new HashMap(globalVariables);
+ }
+ /**
+ * Remove a variable
+ * @param var the variable name
+ */
+ public void removeVariable(String var) throws JET2TagException
+ {
+ globalVariables.remove(var);
+ }
+
+ /**
+ * Test whether a variable is defined
+ * @param var the variable name
+ * @return <code>true</code> if defined, <code>false</code> otherwise.
+ */
+ public boolean hasVariable(String var)
+ {
+ return globalVariables.containsKey(var);
+ }
+
+ /**
+ * @return the tagFactory
+ */
+ public final TagFactory getTagFactory()
+ {
+ return tagFactory;
+ }
+
+ /**
+ * @param tagFactory the tagFactory to set
+ */
+ public final void setTagFactory(TagFactory tagFactory)
+ {
+ this.tagFactory = tagFactory;
+ }
+
+ /**
+ * Extract a list of variables from the context
+ * @param variableNames a comma separated list of variables. May be <code>null</code>.
+ * @return a Map keyed by variable name. Will be <code>null</code> if <code>variableNames</code> is <code>null</code>.
+ * @throws JET2TagException if <code>variableNames</code> contains an invalid variable name.
+ */
+ public Map extractVariables(String variableNames) throws JET2TagException
+ {
+ Map savedVariableValues = null;
+ if (variableNames != null)
+ {
+ savedVariableValues = new HashMap();
+ for (StringTokenizer tokenizer = new StringTokenizer(variableNames, ","); tokenizer.hasMoreTokens();) { //$NON-NLS-1$
+ String varName = tokenizer.nextToken();
+ varName = varName.trim();
+ savedVariableValues.put(varName, getVariable(varName));
+ }
+ }
+ return savedVariableValues;
+ }
+
+ /**
+ * Restore variables in the passed map to the context.
+ * @param savedVariableValues a Map keyed by variable name. If <code>null</code> the method does nothing.
+ * @throws JET2TagException if a variable name is invalid
+ */
+ public void restoreVariables(Map savedVariableValues) throws JET2TagException
+ {
+ if (savedVariableValues != null)
+ {
+ for (Iterator i = savedVariableValues.entrySet().iterator(); i.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry)i.next();
+ setVariable((String)entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * Set the context variables to only the variables in variablesToPass
+ * @param variablesToPass a non-null map keyed by variable name.
+ * @throws JET2TagException if a variable name is invalid
+ */
+ public void setVariables(Map variablesToPass) throws JET2TagException
+ {
+ globalVariables.clear();
+ for (Iterator i = variablesToPass.entrySet().iterator(); i.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry)i.next();
+ String varName = (String)entry.getKey();
+ setVariable(varName, entry.getValue());
+ }
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Template.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Template.java
new file mode 100644
index 0000000..8f82752
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Template.java
@@ -0,0 +1,35 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2Template.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet;
+
+
+/**
+ * Interface to compiled JET2 templates.
+ *
+ */
+public interface JET2Template
+{
+
+ /**
+ * Execute the template against the input contained in the {@link JET2Context},
+ * and writing the result to the {@link JET2Writer}.
+ * @param context the input context. Cannot be <code>null</code>.
+ * @param out the output writer. Cannote be <code>null</code>.
+ */
+ public abstract void generate(JET2Context context, JET2Writer out);
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoader.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoader.java
new file mode 100644
index 0000000..6f1efe1
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoader.java
@@ -0,0 +1,36 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2TemplateLoader.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet;
+
+/**
+ * A loader for templates within a transform.
+ */
+public interface JET2TemplateLoader
+{
+
+ /**
+ * Return an instance of the specified template, or <code>null</code>.
+ * <p>
+ * This class is not typically implemented by clients. The JET2 compiler will
+ * emit an instance of this class for each JET2 project.
+ * </p>
+ * @param templatePath a transform project relative path to the template.
+ * @return the template instance or <code>null</code> if the template cannot be found
+ */
+ public JET2Template getTemplate(String templatePath);
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoaderExtension.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoaderExtension.java
new file mode 100644
index 0000000..e64214d
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2TemplateLoaderExtension.java
@@ -0,0 +1,38 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2TemplateLoaderExtension.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet;
+
+/**
+ * Extension interface for {@link JET2TemplateLoader} allowing
+ * for delegation of template loading to other loaders
+ */
+public interface JET2TemplateLoaderExtension
+{
+ /**
+ * Add a template loader to which this loader will delegate
+ * when it cannot find a template
+ * @param loader a template loader or <code>null</code> to remove the delegate loader.
+ */
+ public abstract void setDelegateLoader(JET2TemplateLoader loader);
+
+ /**
+ * Return the template loader to which this loader will delegate
+ * when it cannot find a template. The initial delegate loader is <code>null</code>.
+ * @return the delegate loader or <code>null</code>.
+ */
+ public abstract JET2TemplateLoader getDelegateLoader();
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Writer.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Writer.java
new file mode 100644
index 0000000..738850e
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/JET2Writer.java
@@ -0,0 +1,194 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2Writer.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet;
+
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Protocol for content writing in JET2 templates.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ * </p>
+ *
+ */
+public interface JET2Writer
+{
+
+ /**
+ * Write the passed string.
+ * @param string a string value.
+ */
+ public abstract void write(String string);
+
+ /**
+ * Write the contents of the passed writer to this writer.
+ * @param bodyContent a writer
+ */
+ public abstract void write(JET2Writer bodyContent);
+
+ /**
+ * Write the passed boolean by calling {@link String#valueOf(boolean)}.
+ * @param b a boolean value
+ */
+ public abstract void write(boolean b);
+
+ /**
+ * Write the passed character by calling {@link String#valueOf(char)}.
+ * @param c a char value
+ */
+ public abstract void write(char c);
+
+ /**
+ * Write the passed character array by calling {@link String#valueOf(char[])}.
+ * @param data an array of characters
+ */
+ public abstract void write(char[] data);
+
+ /**
+ * Write the passed double value by calling {@link String#valueOf(double)}.
+ * @param d a double value
+ */
+ public abstract void write(double d);
+
+ /**
+ * Write the passed float value by calling {@link String#valueOf(float)}.
+ * @param f a float value
+ */
+ public abstract void write(float f);
+
+ /**
+ * Write the passed integer by calling {@link String#valueOf(int)}.
+ * @param i an integer value
+ */
+ public abstract void write(int i);
+
+ /**
+ * Write the passed long value calling {@link String#valueOf(long)}.
+ * @param l a long value.
+ */
+ public abstract void write(long l);
+
+ /**
+ * Write the pass object by calling {@link Object#toString()}.
+ * @param obj an object.
+ */
+ public abstract void write(Object obj);
+
+ /**
+ * Create a writer for handling nested content. The new writer will have access
+ * to all position handlers defined on the parent writer (and its parents)
+ * @return the nested content writer of type {@link BufferedJET2Writer}
+ */
+ public abstract JET2Writer newNestedContentWriter();
+
+ /**
+ * Return the parent of this writer, if it was created via {@link #newNestedContentWriter()}.
+ * @return the parent writer, or <code>null</code>.
+ */
+ public abstract JET2Writer getParentWriter();
+
+ /**
+ * Return the current length (in characters) of the output
+ * @return the current length
+ * @deprecated Use {@link BufferedJET2Writer#getContentLength()}
+ */
+ public abstract int getLength();
+
+ /**
+ * Return the backing IDocument for this writer. Use this method to do advanced
+ * writer processing, such as adding Positions for later re-writing of the document
+ * contents.
+ * @return the backing document
+ * @deprecated Use {@link BufferedJET2Writer}.
+ */
+ public abstract IDocument getDocument();
+
+ /**
+ * Add a listener to the writer life cycle events. The writer records one listener per
+ * category. Subsequent calls to this method with the same category value have no effect.
+ * If the listener was created view {@link #newNestedContentWriter()}, then the listener is added
+ * to the root writer, rather than the listener itself.
+ * @param category the listener category
+ * @param listener a listener
+ * @throws NullPointerException if listener is <code>null</code>.
+ */
+ public abstract void addEventListener(String category, IWriterListener listener);
+
+ /**
+ * Return the registered writer event listeners
+ * @return a possibly empty array of listeners
+ */
+ public abstract IWriterListener[] getEventListeners();
+
+ /**
+ * Convenience method wrapping getDocument().addPositionCategory(String).
+ * @param category a Position Category
+ * @throws IllegalArgumentException wrapping a {@link org.eclipse.jface.text.BadPositionCategoryException}
+ * @see IDocument#addPositionCategory(java.lang.String)
+ * @deprecated Use {@link BufferedJET2Writer#getAdapter(Class)} to return an IDocument, and then
+ * use {@link IDocument#addPositionCategory(String)}.
+ */
+ public abstract void addPositionCategory(String category);
+
+ /**
+ * Convenience method wrapping getDocument().addPosition(String, Position).
+ * Any
+ * {@link org.eclipse.jface.text.BadPositionCategoryException} or
+ * {@link org.eclipse.jface.text.BadLocationException} is wrapped in a
+ * a runtime exception.
+ * @param category a position category
+ * @param position a position
+ * @throws WriterPositionException wrapping a {@link org.eclipse.jface.text.BadPositionCategoryException}
+ * or {@link org.eclipse.jface.text.BadLocationException}
+ * @see IDocument#addPosition(java.lang.String, org.eclipse.jface.text.Position)
+ * @deprecated Use {@link BufferedJET2Writer#getAdapter(Class)} to return an IDocument, and then
+ * use {@link IDocument#addPosition(String, Position)}.
+ */
+ public abstract void addPosition(String category, Position position);
+
+ /**
+ * Convenience method wrapping getDocument().getPositions(String).
+ * Any
+ * {@link org.eclipse.jface.text.BadPositionCategoryException}
+ * is wrapped in a runtime exception.
+ * @param category a position category
+ * @return an array of positions
+ * @throws WriterPositionException wrapping a {@link org.eclipse.jface.text.BadPositionCategoryException}
+ * @see IDocument#getPositions(java.lang.String)
+ * @deprecated Use {@link BufferedJET2Writer#getAdapter(Class)} to return an IDocument, and then
+ * use {@link IDocument#getPositions(String)}.
+ */
+ public abstract Position[] getPositions(String category);
+
+ /**
+ * Convenience method wrapping getDocument().replace(int,int,String).
+ * Any
+ * {@link org.eclipse.jface.text.BadLocationException}
+ * is wrapped in a runtime exception.
+ * @param offset the offset of the text to replace
+ * @param length the length of the text to replace
+ * @param text the replacement text
+ * @throws WriterPositionException wrapping a {@link org.eclipse.jface.text.BadLocationException}
+ * @see IDocument#replace(int, int, java.lang.String)
+ * @deprecated Use {@link BufferedJET2Writer#replaceContent(int, int, String)}.
+ */
+ public abstract void replace(int offset, int length, String text);
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/compiler/JETCompilerOptions.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/compiler/JETCompilerOptions.java
new file mode 100644
index 0000000..8dcb125
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/compiler/JETCompilerOptions.java
@@ -0,0 +1,253 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETCompilerOptions.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.core.compiler;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.ast.JETAST;
+
+/**
+ * Utility class representing JET compiler Options
+ */
+public final class JETCompilerOptions {
+
+ /**
+ * The default value of the {@link #OPTION_COMPILED_TEMPLATE_PACKAGE} option; value: "org.eclipse.jet2.internal.compiled".
+ */
+ public static final String DEFAULT_COMPILED_TEMPLATE_PACKAGE = "org.eclipse.jet.compiled"; //$NON-NLS-1$
+
+ /**
+ * The default value of the {@link #OPTION_COMPILED_TEMPLATE_SRC_DIR} option; value: "jet2java".
+ */
+ public static final String DEFAULT_COMPILED_TEMPLATE_SRC_DIR = "jet2java"; //$NON-NLS-1$
+
+ /**
+ * The default value of the {@link #OPTION_TEMPLATE_EXT} option; value: "jet".
+ */
+ public static final String DEFAULT_TEMPLATE_EXT = "jet"; //$NON-NLS-1$
+
+ /**
+ * The default value of the {@link #OPTION_SET_JAVA_FILES_AS_DERIVED} option (Boolean.TRUE).
+ */
+ public static final Boolean DEFAULT_SET_JAVA_FILES_AS_DERIVED = Boolean.TRUE;
+
+ /**
+ * Common prefix for compile option names
+ */
+ private static final String NS = "org.eclipse.jet."; //$NON-NLS-1$
+
+ /**
+ * Compiler option specifying the package to which compiled templates are written; value: "compiledTemplatePackage".
+ */
+ public static final String OPTION_COMPILED_TEMPLATE_PACKAGE = NS
+ + "compiledTemplatePackage"; //$NON-NLS-1$
+
+ /**
+ * Compiler option specifying the Java project source directory to which compiled templates are written; value: "compiledTemplateSrcDir".
+ */
+ public static final String OPTION_COMPILED_TEMPLATE_SRC_DIR = NS
+ + "compiledTemplateSrcDir"; //$NON-NLS-1$
+
+ /**
+ * Compiler option specifying which extensions are recognized as extensions; value: "templateExt".
+ */
+ public static final String OPTION_TEMPLATE_EXT = NS + "templateExt"; //$NON-NLS-1$
+
+ /**
+ * Compiler option specifying whether generated Java source should be marked as 'derived'.
+ */
+ public static final String OPTION_SET_JAVA_FILES_AS_DERIVED = NS
+ + "setJavaDerived"; //$NON-NLS-1$
+
+ /**
+ * Compiler option specifying which JET specification version to compile; value "jetSpecificationVersion".
+ */
+ public static final String OPTION_JET_SPECIFICATION_VERSION = NS
+ + "jetSpecificationVersion"; //$NON-NLS-1$
+
+ /**
+ * Default value for {@link #OPTION_JET_SPECIFICATION_VERSION}.
+ */
+ public static final Integer DEFAULT_JET_SPECIFICATION_VERSION = new Integer(
+ JETAST.JET_SPEC_V2);
+
+ /**
+ * Compiler option specifying the base locations for JET1 templates to override; value "{@value #OPTION_V1_BASE_TRANSFORMATION}".
+ * Use of this option is discouraged, along with the &lt;%&#064; include %&gt; directive.
+ * <p>
+ * This option is not set globally. It is only set at the project level.
+ * </p>
+ */
+ public static final String OPTION_V1_BASE_TRANSFORMATION = NS
+ + "v1BaseTransformationID"; //$NON-NLS-1$
+
+ /**
+ * Default value for {@link #OPTION_V1_BASE_TRANSFORMATION}.
+ */
+ public static final String DEFAULT_V1_BASE_TRANSFORMATION = ""; //$NON-NLS-1$
+
+ /**
+ * Compiler option specifying whether all base templates are recompiled into the current project; value "{@value #OPTION_V1_COMPILE_BASE_TEMPLATES}".
+ * Use this option when {@link #OPTION_V1_BASE_TRANSFORMATION} is not empty to cause all base templates to be overriden. This will
+ * be necessary if base templates use &lt;%&#064; include %&gt; directives that the current project overrides.
+ */
+ public static final String OPTION_V1_COMPILE_BASE_TEMPLATES = NS
+ + "v1CompileBaseTemplates"; //$NON-NLS-1$
+
+ /**
+ * Default value for {@link #OPTION_V1_COMPILE_BASE_TEMPLATES}; value {@value #DEFAULT_V1_COMPILE_BASE_TEMPLATES}.
+ */
+ public static final Boolean DEFAULT_V1_COMPILE_BASE_TEMPLATES = Boolean.FALSE;
+
+ /**
+ * Compiler option specifying the project relative location of JET V1 templates.
+ */
+ public static final String OPTION_V1_TEMPLATES_DIR = NS + "v1TemplatesDir"; //$NON-NLS-1$
+
+ /**
+ * Default value for {@link #OPTION_V1_TEMPLATES_DIR}; value "{@value #DEFAULT_V1_TEMPLATES_DIR}.
+ */
+ public static final String DEFAULT_V1_TEMPLATES_DIR = "templates"; //$NON-NLS-1$
+
+ /**
+ * Lazily initialized Map containing the default compiler options
+ */
+ static Map defaultCompileOptions = null;
+
+ /**
+ * Lock option used for compiler options initialization
+ */
+ static final Object defaultCompileOptionsLock = new Object();
+
+ /**
+ * Utility class - prevent instantiation
+ *
+ */
+ private JETCompilerOptions() {
+
+ }
+
+ /**
+ * Return the default compiler options
+ * @return an unmodifiable map containing the default compiler options (keys) and their default values
+ * @see #OPTION_COMPILED_TEMPLATE_PACKAGE
+ * @see #OPTION_COMPILED_TEMPLATE_SRC_DIR
+ * @see #OPTION_SET_JAVA_FILES_AS_DERIVED
+ * @see #OPTION_TEMPLATE_EXT
+ */
+ public static Map getDefaultCompilerOptions() {
+ if (defaultCompileOptions == null) {
+ Map map = new HashMap();
+ map.put(OPTION_COMPILED_TEMPLATE_PACKAGE,
+ DEFAULT_COMPILED_TEMPLATE_PACKAGE);
+ map.put(OPTION_COMPILED_TEMPLATE_SRC_DIR,
+ DEFAULT_COMPILED_TEMPLATE_SRC_DIR);
+ map.put(OPTION_TEMPLATE_EXT, DEFAULT_TEMPLATE_EXT);
+ map.put(OPTION_SET_JAVA_FILES_AS_DERIVED,
+ DEFAULT_SET_JAVA_FILES_AS_DERIVED);
+ map.put(OPTION_JET_SPECIFICATION_VERSION,
+ DEFAULT_JET_SPECIFICATION_VERSION);
+ map.put(OPTION_V1_COMPILE_BASE_TEMPLATES,
+ DEFAULT_V1_COMPILE_BASE_TEMPLATES);
+ map.put(OPTION_V1_BASE_TRANSFORMATION,
+ DEFAULT_V1_BASE_TRANSFORMATION);
+ map.put(OPTION_V1_TEMPLATES_DIR, DEFAULT_V1_TEMPLATES_DIR);
+
+ synchronized (defaultCompileOptionsLock) {
+ defaultCompileOptions = Collections.unmodifiableMap(map);
+ }
+ }
+ return defaultCompileOptions;
+ }
+
+ /**
+ * Return the value of a string option, or the default value of the option if not specified in the pass options map
+ * @param options a compiler options map
+ * @param key a compiler option key
+ * @return the option value or default value
+ * @throws NullPointerException if <code>options</code> or <code>key</code> is <code>null</code>
+ * @throws IllegalArgumentException if <code>key</code> is not a know compiler option
+ */
+ public static String getStringOption(Map options, String key) {
+ if (options == null || key == null) {
+ throw new NullPointerException();
+ }
+ if (!getDefaultCompilerOptions().containsKey(key)) {
+ throw new IllegalArgumentException(key);
+ }
+
+ Object value = options.get(key);
+ if (value == null) {
+ value = getDefaultCompilerOptions().get(key);
+ }
+
+ return value == null ? null : value.toString();
+ }
+
+ /**
+ * Return the value of a boolean option, or the default value of the option if not specified in the pass options map
+ * @param options a compiler options map
+ * @param key a compiler option key
+ * @return the option value or default value
+ * @throws NullPointerException if <code>options</code> or <code>key</code> is <code>null</code>
+ * @throws IllegalArgumentException if <code>key</code> is not a know compiler option
+ */
+ public static boolean getBooleanOption(Map options, String key) {
+ if (options == null || key == null) {
+ throw new NullPointerException();
+ }
+ if (!getDefaultCompilerOptions().containsKey(key)) {
+ throw new IllegalArgumentException(key);
+ }
+
+ Object value = options.get(key);
+ if (value == null) {
+ value = getDefaultCompilerOptions().get(key);
+ }
+ return value == null ? false
+ : value instanceof Boolean ? ((Boolean) value).booleanValue()
+ : Boolean.valueOf(value.toString()).booleanValue();
+ }
+
+ /**
+ * Return the value of an integer option, or the default value of the option if not specified in the pass options map
+ * @param options a compiler options map
+ * @param key a compiler option key
+ * @return the option value or default value
+ * @throws NullPointerException if <code>options</code> or <code>key</code> is <code>null</code>
+ * @throws IllegalArgumentException if <code>key</code> is not a know compiler option
+ */
+ public static int getIntOption(Map options,
+ String key) {
+ if (options == null || key == null) {
+ throw new NullPointerException();
+ }
+ if (!getDefaultCompilerOptions().containsKey(key)) {
+ throw new IllegalArgumentException(key);
+ }
+
+ Object value = options.get(key);
+ if (value == null) {
+ value = getDefaultCompilerOptions().get(key);
+ }
+ return value == null ? -1
+ : value instanceof Integer ? ((Integer) value).intValue()
+ : Integer.valueOf(value.toString()).intValue();
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/AbstractTemplateResolver.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/AbstractTemplateResolver.java
new file mode 100644
index 0000000..ef6b7bb
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/AbstractTemplateResolver.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Abstract implementatino of {@link ITemplateResolver}. Extenders must only implement
+ * the following methods.
+ *
+ */
+public abstract class AbstractTemplateResolver implements ITemplateResolver {
+
+ private static final String SLASH = "/"; //$NON-NLS-1$
+
+ private final URI[] baseLocations;
+
+ /**
+ * Create a template resolver taking templates from one or more base locations
+ * @param baseLocations an array of template URIs.
+ */
+ protected AbstractTemplateResolver(URI[] baseLocations) {
+ if(baseLocations == null) {
+ throw new NullPointerException();
+ }
+ // make a defensive copy of base locations.
+ this.baseLocations = new URI[baseLocations.length];
+ for (int i = 0; i < baseLocations.length; i++) {
+ if(baseLocations[i] == null) {
+ throw new NullPointerException();
+ }
+ // make sure all URI's end in a SLASH
+ this.baseLocations[i] = baseLocations[i].toString().endsWith(SLASH)
+ ? baseLocations[i]
+ : baseLocations[i].resolve(baseLocations[i].getPath() + "/"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Create a template resolver taking templates from a single base location.
+ * @param baseLocation a base location URI
+ */
+ protected AbstractTemplateResolver(URI baseLocation) {
+ this(new URI[] {baseLocation});
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.ITemplateResolver#getBaseLocations()
+ */
+ public final URI[] getBaseLocations() {
+ final URI[] tempArray = new URI[baseLocations.length];
+ // return a defensive copy
+ System.arraycopy(baseLocations, 0, tempArray, 0, baseLocations.length);
+
+ return tempArray;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.ITemplateResolver#getIncludedInput(java.lang.String, org.eclipse.jet.tools.parser.ITemplateInput[])
+ */
+ public final ITemplateInput getIncludedInput(String includePath,
+ ITemplateInput[] activeInputs) throws RecursiveIncludeException {
+ if(includePath == null || activeInputs == null) {
+ throw new NullPointerException();
+ }
+ if(activeInputs.length == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ String templatePath = resolveToTemplatePath(includePath, activeInputs[activeInputs.length - 1].getTemplatePath());
+
+ int baseLocationsStartIndex = 0;
+
+ boolean recursiveInclude = templatePath.equals(activeInputs[activeInputs.length - 1].getTemplatePath());
+ if(recursiveInclude) {
+ final URI currentBaseURI = activeInputs[activeInputs.length - 1].getBaseLocation();
+
+ baseLocationsStartIndex = 1 + findBaseLocationIndex(currentBaseURI);
+ }
+
+ for (int i = baseLocationsStartIndex; i < baseLocations.length; i++) {
+ // check for recursive input
+ for (int j = 0; j < activeInputs.length; j++) {
+ if(templatePath.equals(activeInputs[j].getTemplatePath())
+ && baseLocations[i].equals(activeInputs[j].getBaseLocation())) {
+ throw new RecursiveIncludeException(templatePath, baseLocations[i], activeInputs);
+ }
+ }
+ if(inputExists(baseLocations[i], templatePath)) {
+ return createTemplateInput(baseLocations[i], templatePath);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Resolve an include path, which is relative to the current file
+ * into a template path, which is relative to the templates base locations
+ * @param includePath the include path
+ * @param templatePath the template path of the current file
+ * @return the templatePath of the include path.
+ * @throws IllegalArgumentException if templatePath cannot be converted to a URI
+ */
+ private String resolveToTemplatePath(String includePath, String templatePath) {
+ try {
+ URI currentURI = new URI(templatePath);
+ URI resolvedURI = currentURI.resolve(includePath);
+ return resolvedURI.toString();
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Return the index of the base passed base location in {@link #baseLocations}.
+ * @param currentBaseURL the current base location
+ * @return the zero-based index.
+ * @throws IllegalArgumentException if currentBaseURL cannot be found
+ */
+ private int findBaseLocationIndex(URI currentBaseURL) {
+ for (int i = 0; i < baseLocations.length; i++) {
+ if(currentBaseURL.equals(baseLocations[i])) {
+ return i;
+ }
+ }
+ throw new IllegalArgumentException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.ITemplateResolver#getInput(java.lang.String)
+ */
+ public final ITemplateInput getInput(String templatePath) {
+ for (int i = 0; i < baseLocations.length; i++) {
+ if(inputExists(baseLocations[i], templatePath)) {
+ return createTemplateInput(baseLocations[i], templatePath);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Test whether the template input exists. The default implementation opens in input
+ * stream to confirme the existance
+ * @param baseLocation the baseLocation
+ * @param templatePath the template path
+ * @return <code>true</code> if {@link #createTemplateInput(URI, String)}} will succeed, <code>false</code> otherwise.
+ */
+ protected abstract boolean inputExists(URI baseLocation, String templatePath);
+
+ /**
+ * Create a Template Input from the give base location and templatePath
+ * @param baseLocation a base loction
+ * @param templatePath a templatePath
+ * @return the template input.
+ */
+ protected abstract ITemplateInput createTemplateInput(URI baseLocation, String templatePath);
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/DefaultTemplateResolver.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/DefaultTemplateResolver.java
new file mode 100644
index 0000000..019e890
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/DefaultTemplateResolver.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jet.internal.core.parser.DefaultTemplateResolverHelper;
+
+/**
+ * Default implementation of {@link ITemplateResolver} that is independent of
+ * the Eclipse workspace. By specifying a {@link ITemplateResolverHelperFactory},
+ * template resolution helpers {@link ITemplateResolverHelper} instances may be
+ * created that leverage knowledge in the eclipse workspace.
+ *
+ */
+public final class DefaultTemplateResolver extends AbstractTemplateResolver
+ implements ITemplateResolver {
+
+ private static final String SLASH = "/"; //$NON-NLS-1$
+
+ private static final ITemplateResolverHelperFactory defaultFactory = new ITemplateResolverHelperFactory() {
+
+ public ITemplateResolverHelper getTemplateResolverHelper(
+ URI baseLocation) {
+ return new DefaultTemplateResolverHelper(baseLocation);
+ }
+
+ };
+
+ /**
+ * Builder for DefaultTemplateResolver
+ *
+ */
+ public static final class Builder {
+
+ private final URI[] baseLocations;
+
+ private ITemplateResolverHelperFactory helper;
+
+ /**
+ * Create an input manager drawing templates from the listed based
+ * locations URLs.
+ *
+ * @param baseLocations
+ * an array of base template locations, in the desired search
+ * order
+ * @throws NullPointerException
+ * if <code>baseLocations</code> or any of its elements is
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * if any baseLocation is not a directory URL (ends in a
+ * "/").
+ */
+ public Builder(URI[] baseLocations) {
+ if (baseLocations == null) {
+ throw new NullPointerException();
+ }
+ for (int i = 0; i < baseLocations.length; i++) {
+ if (baseLocations[i] == null) {
+ throw new NullPointerException();
+ }
+ if (!baseLocations[i].getPath().endsWith(SLASH)) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ this.baseLocations = baseLocations;
+ }
+
+ /**
+ * Convenience constructor when templates are drawn from one location
+ * only
+ *
+ * @param baseLocation
+ * the base location of templates.
+ * @throws NullPointerException
+ * if <code>baseLocation</code> is <code>null</code>.
+ */
+ public Builder(URI baseLocation) {
+ this(new URI[] { baseLocation });
+ }
+
+ /**
+ * Create the input manager instance described by the builder
+ *
+ * @return the {@link DefaultTemplateResolver} instance.
+ */
+
+ public Builder templateResolverHelperFactory(
+ ITemplateResolverHelperFactory helper) {
+ this.helper = helper;
+ return this;
+ }
+
+ /**
+ * Builder the template resolver
+ *
+ * @return the {@link DefaultTemplateResolver} instance
+ */
+ public ITemplateResolver build() {
+ return new DefaultTemplateResolver(this);
+ }
+
+ }
+
+ private final Map delegateByURI = new HashMap();
+
+ private DefaultTemplateResolver(Builder builder) {
+ super(builder.baseLocations);
+ final URI[] uris = getBaseLocations();
+ for (int i = 0; i < uris.length; i++) {
+ final URI baseLocation = uris[i];
+ ITemplateResolverHelper helper = getTemplateResolverHelper(
+ builder.helper, baseLocation);
+ delegateByURI.put(baseLocation, helper);
+ }
+ }
+
+ private ITemplateResolverHelper getTemplateResolverHelper(
+ ITemplateResolverHelperFactory helperFactory, URI baseLocation) {
+ ITemplateResolverHelper helper = helperFactory == null ? null
+ : helperFactory.getTemplateResolverHelper(baseLocation);
+
+ if (helper == null) {
+ helper = defaultFactory.getTemplateResolverHelper(baseLocation);
+ }
+ return helper;
+ }
+
+ /**
+ * Create a Template Input from the give base location and templatePath
+ *
+ * @param baseLocation
+ * a base loction
+ * @param templatePath
+ * a templatePath
+ * @return the template input.
+ */
+ protected ITemplateInput createTemplateInput(URI baseLocation,
+ String templatePath) {
+ ITemplateResolverHelper helper = (ITemplateResolverHelper) delegateByURI
+ .get(baseLocation);
+ return helper.createTemplateInput(templatePath);
+ }
+
+ /**
+ * Test whether the template input exists. The default implementation opens
+ * in input stream to confirme the existance
+ *
+ * @param baseLocation
+ * the baseLocation
+ * @param templatePath
+ * the template path
+ * @return <code>true</code> if {@link #createTemplateInput(URI, String)}}
+ * will succeed, <code>false</code> otherwise.
+ */
+ protected boolean inputExists(URI baseLocation, String templatePath) {
+ ITemplateResolverHelper helper = (ITemplateResolverHelper) delegateByURI
+ .get(baseLocation);
+ return helper.inputExists(templatePath);
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IJETParser.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IJETParser.java
new file mode 100644
index 0000000..9156cb2
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IJETParser.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+
+/**
+ * Define the behavior of a JET Parser
+ */
+public interface IJETParser {
+ /**
+ * Parse the named input template
+ * @param templatePath the template Path
+ * @return the root of the AST
+ */
+ public Object parse(String templatePath);
+
+ /**
+ * Parse contents as a JET template
+ * @param template the template
+ * @return the root of the AST
+ */
+ public Object parse(char[] template);
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IProblem.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IProblem.java
new file mode 100644
index 0000000..fe99132
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/IProblem.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+/**
+ * Describe a parsing problem
+ *
+ * This interface is not intended to be implemented by clients
+ */
+public interface IProblem {
+ /**
+ * Error Id for an XML end tag that has no corresponding start tag.
+ * @see #getId()
+ */
+ public static final int MissingXmlStartTag = 1;
+
+ /**
+ * Error Id for an XML start tag that has no corresponding end tag.
+ * @see #getId()
+ */
+ public static final int MissingXmlEndTag = 2;
+
+ /**
+ * Error Id for an XML tag or JET directive that is missing a required attribute
+ * @see #getId()
+ */
+ public static final int MissingRequiredAttribute = 3;
+
+ /**
+ * Error Id for taglib directive that defines a prefix defined by a preceding taglib directive
+ * @see #getId()
+ */
+ public static final int DuplicateXMLNamespacePrefix = 4;
+
+ /**
+ * Error Id for taglib directive that defines references an unknown tag library id
+ * @see #getId()
+ */
+ public static final int UnknownTagLibrary = 5;
+
+ /**
+ * Error Id for an attribute that is not defined in the tag definition
+ */
+ public static final int UnknownAttributeInTag = 6;
+
+ /**
+ * Represent an unterminated XML Tag
+ */
+ public static final int UnterminatedXMLTag = 7;
+
+ /**
+ * Represent a duplicate attribute in an XML Tag;
+ */
+ public static final int DuplicateAttribute = 8;
+
+ /**
+ * An underlying JETException was thrown by the JET parser
+ */
+ public static final int JETException = 9;
+
+ /**
+ * Two templates specify that they compile to the same Java Class
+ */
+ public static final int MultipleTemplatesWithSameJavaClass = 10;
+
+ /**
+ * Use of an attribute that has been deprecated.
+ */
+ public static final int DeprecatedAttribute = 11;
+
+ /**
+ * Tag may not have a body - the tag must be of the form &lt;tagName/&gt;.
+ */
+ public static final int TagCannotHaveContent = 12;
+
+ /**
+ * Tag must have content - the tag must be of the form &lt;tagName&gt;xxx&lt;/tagName&gt;.
+ */
+ public static final int TagCannotBeEmpty = 13;
+
+ /**
+ * Use of the tag has been deprecated.
+ */
+ public static final int DeprecatedTag = 14;
+
+ /**
+ * Unsupported Directive.
+ */
+ public static final int UnsupportedDirective = 15;
+
+ /**
+ * A tag that has a known tag library prefix, but is not a recognized name. Usually
+ * indicates a typographical error.
+ */
+ public static final int UnknownXMLTag = 16;
+
+ /**
+ * A tag this is declared as an 'emptyTag' occured as &lt;tag ...&gt;, and has been
+ * interpreted as the equivalent empty tag &lt;tag .../&gt;.
+ */
+ public static final int TagInterpretedAsEmptyTag = 17;
+
+ /**
+ * An &at;include directive could not resolve a referenced file.
+ */
+ public static final int MissingFile = 18;
+
+ /**
+ * A &lt;%&at; start %&gt; directive was found other than after a &lt;%&at; include fail="alternative" %&gt; directive.
+ */
+ public static final int StartDirectiveOutOfContext = 19;
+
+ /**
+ * A &lt;%&at; end %&gt; directive was found other than after a &lt;%&at; include fail="alternative" %&gt; and &lt;%&at; start %&gt; directive.
+ */
+ public static final int EndDirectiveOutOfContext = 20;
+
+ /**
+ * An &lt;%&at; include fail="alternative" %&gt; directive has a &lt;%&at; start %&gt; directive, but no &lt;%&at; end %&gt; directive.
+ */
+ public static final int MissingEndDirective = 21;
+
+ /**
+ * An &lt;%&at; include fail="alternative" %&gt; directive has not &lt;%&at; start %&gt; and &lt;%&at; end %&gt; directives.
+ */
+ public static final int MissingIncludeAlternative = 22;
+
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITagLibraryResolver.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITagLibraryResolver.java
new file mode 100644
index 0000000..f1daa94
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITagLibraryResolver.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import org.eclipse.jet.taglib.TagLibrary;
+
+/**
+ * Protocol for resolving Tag library ids into instances of {@link TagLibrary}
+ *
+ */
+public interface ITagLibraryResolver {
+
+ /**
+ * Return a TagLibrary instance given a tag library ID
+ * @param tagLibraryID a tag library id
+ * @return a {@link TagLibrary} instance or <code>null</code> if the library ID is not known to the resolver
+ */
+ public abstract TagLibrary getLibrary(String tagLibraryID);
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateInput.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateInput.java
new file mode 100644
index 0000000..f11b932
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateInput.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import java.io.Reader;
+import java.net.URI;
+
+/**
+ * Encapsulate a JET Templates input
+ * @since 1.0
+ */
+public interface ITemplateInput {
+ /**
+ * Return the template Path of the input.
+ * @return the template Path
+ */
+ public String getTemplatePath();
+
+ /**
+ * Return the base URL of the input
+ * @return the template base URL
+ */
+ public URI getBaseLocation();
+
+ /**
+ * Return a reader for the template input.
+ * It is the responsibility of the caller to close the reader
+ * @return a reader
+ */
+ public Reader getReader() throws TemplateInputException;
+
+ /**
+ * Return the encoding of the template input
+ * @return the encoding or <code>null</code> if not known
+ * @throws TemplateInputException if an error occurs while determing the encoding
+ */
+ public String getEncoding() throws TemplateInputException;
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolver.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolver.java
new file mode 100644
index 0000000..8c8b268
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import java.net.URI;
+
+
+
+/**
+ * Protocol for resolving JET template paths into actual input - used by the JET parser and compiler.
+ */
+public interface ITemplateResolver {
+ /**
+ * Return the template input given a template path.
+ * @param templatePath the JET template path
+ * @return the template input, or <code>null</code>
+ */
+ public ITemplateInput getInput(String templatePath);
+
+ /**
+ * Return the appropriate template input, given a template path, and the current stack of
+ * template inputs.
+ * The activeInputs argument is order from initial input to most recently included input.
+ * @param templatePath the JET template path
+ * @param activeInputs the input stack.
+ * @return the template input, or <code>null</code>
+ * @throws RecursiveIncludeException if including templatePath would result in a recursive loop
+ * @throws NullPointerException if either argument is <code>null</code>
+ * @throws IllegalArgumentException if <code>inputStack</code> does not contain at least one element.
+ */
+ public ITemplateInput getIncludedInput(String templatePath, ITemplateInput[] activeInputs) throws RecursiveIncludeException;
+
+ /**
+ * Return the base locations from which the template resolver will load templates
+ * @return a non-empty array of base location URIs.
+ */
+ public URI[] getBaseLocations();
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelper.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelper.java
new file mode 100644
index 0000000..bba4503
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelper.java
@@ -0,0 +1,44 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: ITemplateResolverHelper.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.core.parser;
+
+
+/**
+ * Protocal for template resolver that is responsible for resolving templates from
+ * a single base location which is communicated to the helper on creation.
+ * Helpers are called by {@link DefaultTemplateResolver} implementations.
+ * @see ITemplateResolverHelperFactory
+ */
+public interface ITemplateResolverHelper {
+
+ /**
+ * Test if templatePath exists
+ * @param templatePath a template Path
+ * @return <code>true</code>
+ */
+ boolean inputExists(String templatePath);
+
+ /**
+ * Create a template input for the given templatePath. The templatePath
+ * will have been valided with {@link #inputExists(String)} prior to
+ * calling this method
+ * @param templatePath a template path
+ * @return the template input object
+ */
+ ITemplateInput createTemplateInput(String templatePath);
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelperFactory.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelperFactory.java
new file mode 100644
index 0000000..653040a
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ITemplateResolverHelperFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * Protocol for a factory that creates {@link ITemplateResolverHelper} instances.
+ * A helper factory may be optionally passed to a {@link DefaultTemplateResolver} instance
+ * during creation to allow for resolution template base URIs in ways other than
+ * using {@link URL#openConnection()}. An example is mapping a base URI to the eclipse workspace
+ * and using the workspace APIs instead.
+ *
+ */
+public interface ITemplateResolverHelperFactory {
+
+ /**
+ * Return a template resolver helper for the base location.
+ * If no appropriate helper can be returned, return <code>null</code>.
+ * @param baseLocation a template base location
+ * @return a {@link ITemplateResolverHelper} instance or <code>null</code>
+ */
+ public abstract ITemplateResolverHelper getTemplateResolverHelper(URI baseLocation);
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ProblemSeverity.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ProblemSeverity.java
new file mode 100644
index 0000000..d950442
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ProblemSeverity.java
@@ -0,0 +1,55 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: ProblemSeverity.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.core.parser;
+
+
+/**
+ * Enumeration of problem Severities
+ * <p>
+ * This class is not intended to be subclassed by clients.
+ * </p>
+ *
+ * @since 1.0
+ */
+public final class ProblemSeverity
+{
+
+ /**
+ * The compilation unit has a problem, but it is will not prevent execution.
+ */
+ public static final ProblemSeverity WARNING = new ProblemSeverity("WARNING"); //$NON-NLS-1$
+
+ /**
+ * The compilation unit has a problem that will prevent execution.
+ */
+ public static final ProblemSeverity ERROR = new ProblemSeverity("ERROR"); //$NON-NLS-1$
+
+ private final String display;
+
+ protected ProblemSeverity(String display)
+ {
+ this.display = display;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return display;
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/RecursiveIncludeException.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/RecursiveIncludeException.java
new file mode 100644
index 0000000..fd18555
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/RecursiveIncludeException.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+import java.net.URI;
+
+
+/**
+ * Describes a recursive inclusion.
+ *
+ * @see ITemplateResolver#getIncludedInput(String, ITemplateInput[])
+ *
+ */
+public final class RecursiveIncludeException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3796819381247376434L;
+
+ private final String templatePath;
+ private final URI baseLocation;
+ private final String[] activeTemplatePaths;
+ private final URI[] activeBaseLocations;
+
+ public RecursiveIncludeException(String templatePath, URI baseLocation, ITemplateInput[] activeInputs) {
+ this.templatePath = templatePath;
+ this.baseLocation = baseLocation;
+ this.activeTemplatePaths = new String[activeInputs.length];
+ this.activeBaseLocations = new URI[activeInputs.length];
+ for (int i = 0; i < activeInputs.length; i++) {
+ activeTemplatePaths[i] = activeInputs[i].getTemplatePath();
+ activeBaseLocations[i] = activeInputs[i].getBaseLocation();
+ }
+ }
+
+ /**
+ * Return the base locations of the active templates.
+ * Base locations are returned in order of inclusion.
+ * That is, the base location of the most recently included
+ * template is the last element in the array.
+ * @return an array of base locations
+ * @see #getActiveTemplatePaths()
+ */
+ public URI[] getActiveBaseLocations() {
+ return activeBaseLocations;
+ }
+
+ /**
+ * Return the template paths of the active templates.
+ * Template paths are returned in the order of inclusion.
+ * That is, the template path of the most recently included
+ * template is the last element in the array.
+ * @return an array of template paths
+ */
+ public String[] getActiveTemplatePaths() {
+ return activeTemplatePaths;
+ }
+
+ /**
+ * Return the base location at which the recursion was detected.
+ * @return the base location.
+ */
+ public URI getBaseLocation() {
+ return baseLocation;
+ }
+
+ /**
+ * Return the template path that would have caused the recursion.
+ * @return the templatePath
+ */
+ public String getTemplatePath() {
+ return templatePath;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/TemplateInputException.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/TemplateInputException.java
new file mode 100644
index 0000000..4035485
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/TemplateInputException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 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.jet.core.parser;
+
+/**
+ * Wrap an exception from a ITemplateInput
+ *
+ */
+public class TemplateInputException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4844450071476080161L;
+
+ /**
+ * Construct a new exception with the specified message and cause.
+ *
+ * Delegates to {@link Exception#Exception(String, Throwable)}
+ * @param message the message
+ * @param cause the cause
+ */
+ public TemplateInputException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Construct a new exception with the specified message.
+ * Delegates to {@link Exception#Exception(String)}.
+ * @param message the detailed message
+ */
+ public TemplateInputException(String message) {
+ super(message);
+ }
+
+ /**
+ * Construct a new exception with the specified cause.
+ * Delegates to {@link Exception#Exception(Throwable)}.
+ * @param cause the cause
+ */
+ public TemplateInputException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElement.java
new file mode 100644
index 0000000..be62517
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElement.java
@@ -0,0 +1,42 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: BodyElement.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * Abstract element representing elements in the body of a compilation unit or another tag.
+ *
+ * <p>
+ * This class is not intended to be subclassed by clients
+ * </p>
+ *
+ */
+public abstract class BodyElement extends JETASTElement {
+
+
+ /**
+ * initialize a BodyElement
+ * @param ast the parent AST
+ * @param line the start line of the element
+ * @param column the start column
+ * @param start the zero-based offset of the first char of the element from the start of the document
+ * @param end the zero-based offset of the next char after the element from the start of the document
+ */
+ protected BodyElement(JETAST ast, int line, int column, int start, int end) {
+ super(ast, line, column, start, end);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElements.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElements.java
new file mode 100644
index 0000000..3b8baea
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/BodyElements.java
@@ -0,0 +1,119 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: BodyElements.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Container class core JET AST elements contained in another AST elements in
+ * their body.
+ *
+ */
+class BodyElements {
+
+ private List bodyElements = new ArrayList();
+
+ private final JETASTElement owner;
+
+ /**
+ * Create a new instance
+ *
+ * @param owner
+ * The owner of the body elements
+ */
+ public BodyElements(JETASTElement owner) {
+ super();
+ this.owner = owner;
+ }
+
+ /**
+ * Return a list of elements in the body
+ *
+ * @return a {@link List} of elements, the empty list if there are no
+ * elements.
+ */
+ public List getBodyElements() {
+ return Collections.unmodifiableList(bodyElements);
+ }
+
+ /**
+ * Add a body element to the body
+ *
+ * @param bodyElement
+ * the text to add
+ */
+ public void addBodyElement(BodyElement bodyElement) {
+ bodyElements.add(bodyElement);
+ bodyElement.setParent(owner);
+ }
+
+ /**
+ * @return Returns the owner.
+ */
+ public final JETASTElement getOwner() {
+ return owner;
+ }
+
+ /**
+ * Return the element after the passed element
+ *
+ * @param element
+ * an element in the list
+ * @return the next element or <code>null</code>
+ * @throws IllegalArgumentException
+ * if element is not in the body
+ */
+ public BodyElement elementAfter(JETASTElement element) {
+ BodyElement nextElement = null;
+ if (bodyElements == null) {
+ throw new IllegalArgumentException();
+ }
+ int index = bodyElements.indexOf(element);
+ if (index == -1) {
+ throw new IllegalArgumentException();
+ }
+
+ if (index + 1 < bodyElements.size()) {
+ nextElement = (BodyElement) bodyElements.get(index + 1);
+ }
+ return nextElement;
+ }
+
+ /**
+ *
+ * @param element
+ * @return
+ */
+ public BodyElement elementBefore(JETASTElement element) {
+ BodyElement prevElement = null;
+ if (bodyElements == null) {
+ throw new IllegalArgumentException();
+ }
+ int index = bodyElements.indexOf(element);
+ if (index == -1) {
+ throw new IllegalArgumentException();
+ }
+
+ if (index > 0) {
+ prevElement = (BodyElement) bodyElements.get(index - 1);
+ }
+ return prevElement;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Comment.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Comment.java
new file mode 100644
index 0000000..24dc3d5
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Comment.java
@@ -0,0 +1,85 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: Comment.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * An JET AST element representing a comment
+ *
+ */
+public final class Comment extends BodyElement {
+
+ private final int commentStart;
+
+ private final int commentEnd;
+
+ private final char[] comment;
+
+ /**
+ * Create a comment element
+ * @param ast the root AST object to to which the comment will be long
+ * @param line the start line of the comment
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset (doc relative) of the comment
+ * @param end the end offset of the comment
+ * @param commentStart the start offset of the comment text (doc relative)
+ * @param commentEnd the end offset of the comment text (doc relative)
+ * @param comment the comment text
+ */
+ Comment(JETAST ast, int line, int colOffset, int start, int end,
+ int commentStart, int commentEnd, char[] comment) {
+ super(ast, line, colOffset, start, end);
+ this.commentStart = commentStart;
+ this.commentEnd = commentEnd;
+ this.comment = comment;
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected final void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+
+ }
+
+ /**
+ * @return Returns the commentEnd.
+ */
+ public int getCommentEnd() {
+ return commentEnd;
+ }
+
+ /**
+ * @return Returns the commentStart.
+ */
+ public int getCommentStart() {
+ return commentStart;
+ }
+
+ /**
+ * Return the comment text
+ * @return the comment text
+ */
+ public String getCommentText() {
+ return new String(comment);
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/IncludedContent.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/IncludedContent.java
new file mode 100644
index 0000000..2285257
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/IncludedContent.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 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.jet.core.parser.ast;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container for content included as a result of a JET V1 &lt;%&#064;include %&gt; directive.
+ *
+ */
+public final class IncludedContent extends BodyElement {
+
+ private final URI baseLocationURI;
+ private BodyElements bodyElements = null;
+
+ private final String templatePath;
+
+ /**
+ * @param ast
+ * @param line
+ * @param column
+ * @param start
+ * @param end
+ */
+ IncludedContent(JETAST ast, String templatePath, URI baseLocationURI, int line, int column, int start, int end) {
+ super(ast, line, column, start, end);
+ this.templatePath = templatePath;
+ this.baseLocationURI = baseLocationURI;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.ast.JETASTElement#accept(org.eclipse.jet.tools.parser.ast.IJETASTVisitor)
+ */
+ protected final void accept0(JETASTVisitor visitor) {
+ final boolean visitChildren = visitor.visit(this);
+ if (visitChildren) {
+ for (Iterator i = getBodyElements().iterator(); i.hasNext();) {
+ JETASTElement element = (JETASTElement) i.next();
+ element.accept0(visitor);
+ }
+ }
+ visitor.endVisit(this);
+ }
+
+ /**
+ * Return the base location URI of the template
+ * @return a location URI
+ */
+ public URI getBaseLocationURI() {
+ return baseLocationURI;
+ }
+
+ /**
+ * Return a read-only list of JET elements contained by this element.
+ * @return a List of {@link JETASTElement} instances. The empty list is returned if there are no elements.
+ */
+ public final List getBodyElements() {
+ if (bodyElements == null) {
+ return Collections.EMPTY_LIST;
+ } else {
+ return bodyElements.getBodyElements();
+ }
+ }
+
+ /**
+ * Return the template Path of the include content
+ * @return the template Path
+ */
+ public String getTemplatePath() {
+ return templatePath;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.ast.JETASTElement#removeLineWhenOtherwiseEmpty()
+ */
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return false;
+ }
+
+ /**
+ * Add an JET AST element to the body of the include
+ * @param bodyElement
+ */
+ public void addBodyElement(BodyElement bodyElement) {
+ getInternalBodyElements().addBodyElement(bodyElement);
+
+ }
+
+
+ /**
+ * Find the element after the given element in the directly contained elements
+ * @param element a JET AST element
+ * @return a {@link BodyElement} or <code>null</code>
+ */
+ public BodyElement elementAfter(JETASTElement element) {
+ return bodyElements.elementAfter(element);
+ }
+
+ /**
+ * Find the element before the given element in the directly contained elements
+ * @param element a JET AST element
+ * @return a {@link BodyElement} or <code>null</code>
+ */
+ public BodyElement elementBefore(JETASTElement element) {
+ return bodyElements.elementBefore(element);
+ }
+
+ /**
+ * Return a object that allows writable access to the JET2 elements contained by this element.
+ * @return a BodyElements instance
+ */
+ BodyElements getInternalBodyElements() {
+ if (bodyElements == null) {
+ bodyElements = new BodyElements(this);
+ }
+ return bodyElements;
+ }
+
+ public JETASTElement getNextElement() {
+
+ if (getBodyElements().size() > 0) {
+ return (JETASTElement) getBodyElements().get(0);
+ } else {
+ return super.getNextElement();
+ }
+ }
+
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETAST.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETAST.java
new file mode 100644
index 0000000..2c038c7
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETAST.java
@@ -0,0 +1,231 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETAST.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.eclipse.jet.taglib.TagDefinition;
+
+/**
+ * The root object of JET AST trees and a factory for JET nodes in that tree.
+ * @since 0.8.0
+ *
+ */
+public final class JETAST {
+
+ /**
+ * Compile to the original JET specification (org.eclipse.emf.codegen).
+ * @see JETASTParser
+ */
+ public static final int JET_SPEC_V1 = 1;
+
+ /**
+ * Compile according to the JET2 specification (org.eclipse.jet).
+ * @see JETASTParser
+ */
+ public static final int JET_SPEC_V2 = 2;
+
+ /**
+ * Create an instance
+ */
+ public JETAST() {
+ // do nothing
+ }
+
+ /**
+ * Create a new, unparented JET2Compilation unit that is not based on a template reference
+ * @return the compilation unit node
+ * @since 0.8.0
+ */
+ public JETCompilationUnit newJETCompilationUnit() {
+ return new JETCompilationUnit(this, null, "", null); //$NON-NLS-1$
+ }
+
+ /**
+ * Create a new unparented JET2Compilation unit for the given template in the given base location
+ * @param baseLocation
+ * @param templatePath
+ * @param encoding the template encoding
+ * @return the compilation unit node
+ * @since 0.8.0
+ */
+ public JETCompilationUnit newJETCompilationUnit(URI baseLocation,
+ String templatePath, String encoding) {
+ return new JETCompilationUnit(this, baseLocation, templatePath,
+ encoding);
+ }
+
+ /**
+ * Create a text element in the AST
+ * @param chars the text
+ * @return the new element
+ */
+ public TextElement newTextElement(char[] chars) {
+ return new TextElement(this, chars);
+ }
+
+ /**
+ * Create a new Directive element in the AST
+ * @param line the start line
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset (doc relative)
+ * @param end the end offset (doc relative)
+ * @param directiveName the directive name
+ * @param attributes a map off attribute names and values
+ * @return the new element
+ */
+ public JETDirective newJETDirective(int line, int colOffset, int start,
+ int end, String directiveName, Map attributes) {
+ return new JETDirective(this, line, colOffset, start, end,
+ directiveName, attributes);
+ }
+
+ /**
+ * Create a new Java Expression element in the AST
+ * @param line the start line
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the offset of the expression element (doc relative)
+ * @param end the end offset of the expression element (doc relative)
+ * @param javaStart the offset of the Java code (doc relative)
+ * @param javaEnd the end offset of the Java code
+ * @param javaContent the Java content
+ * @return the new element
+ */
+ public JavaExpression newJavaExpression(int line, int colOffset, int start,
+ int end, int javaStart, int javaEnd, char[] javaContent) {
+ return new JavaExpression(this, line, colOffset, start, end, javaStart,
+ javaEnd, javaContent);
+ }
+
+ /**
+ * Create a new Java Scriptlet element in the AST
+ * @param line the start line
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the offset of the scriptlet element (doc relative)
+ * @param end the end offset of the scriplet element (doc relative)
+ * @param javaStart the offset of the Java code (doc relative)
+ * @param javaEnd the end offset of the Java code
+ * @param javaContent the Java content
+ * @return the new element
+ */
+ public JavaScriptlet newJavaScriptlet(int line, int colOffset, int start,
+ int end, int javaStart, int javaEnd, char[] javaContent) {
+ return new JavaScriptlet(this, line, colOffset, start, end, javaStart,
+ javaEnd, javaContent);
+ }
+
+ /**
+ * Create a new Java Declaration element in the AST
+ * @param line the start line
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the offset of the declaration element (doc relative)
+ * @param end the end offset of the declaration element (doc relative)
+ * @param javaStart the offset of the Java code (doc relative)
+ * @param javaEnd the end offset of the Java code
+ * @param javaContent the Java content
+ * @return the new element
+ */
+ public JavaDeclaration newJavaDeclaration(int line, int colOffset,
+ int start, int end, int javaStart, int javaEnd, char[] javaContent) {
+ return new JavaDeclaration(this, line, colOffset, start, end,
+ javaStart, javaEnd, javaContent);
+ }
+
+ /**
+ * Create a new empty XML element in the AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param tagName the QName of the element
+ * @param attributeMap a Map off element attribute names and values
+ * @param td the TagDefinition of the element
+ * @return the new element
+ */
+ public XMLEmptyElement newXMLEmptyElement(int line, int colOffset,
+ int start, int end, String tagName, Map attributeMap,
+ TagDefinition td) {
+ return new XMLEmptyElement(this, line, colOffset, start, end, tagName,
+ attributeMap, td);
+ }
+
+ /**
+ * Create a new XML element with body in the AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param tagName the QName of the element
+ * @param attributeMap a Map off element attribute names and values
+ * @param td the TagDefinition of the tag
+ * @return the new element
+ */
+ public XMLBodyElement newXMLBodyElement(int line, int colOffset,
+ int start, int end, String tagName, Map attributeMap,
+ TagDefinition td) {
+ return new XMLBodyElement(this, line, colOffset, start, end, tagName,
+ attributeMap, td);
+ }
+
+ /**
+ * Create a new Comment element in the AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param commentStart the start offset of the comment text (doc relative)
+ * @param commentEnd the end offset of the comment text (doc relative)
+ * @param comment the comment text
+ * @return the new element
+ */
+ public Comment newComment(int line, int colOffset, int start, int end,
+ int commentStart, int commentEnd, char[] comment) {
+ return new Comment(this, line, colOffset, start, end, commentStart,
+ commentEnd, comment);
+ }
+
+ /**
+ * Create a new InludedContent element in the AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param baseLocation the base Location URI of the resolved include
+ * @param templatePath the templatePath (relative to the base location
+ * @return the IncludedContent element
+ */
+ public IncludedContent newIncludedContent(int line, int colOffset, int start, int end, URI baseLocation, String templatePath) {
+ return new IncludedContent(this, templatePath, baseLocation, line, colOffset, start, end);
+ }
+
+ /**
+ * Create a new XML end element corresponding to the end of a XMLBodyElement.
+ * @param line
+ * @param col
+ * @param start
+ * @param end
+ * @param tagName
+ * @param startTag
+ * @return
+ */
+ public XMLBodyElementEnd newXMLBodyElementEnd(int line, int col,
+ int start, int end, String tagName) {
+ return new XMLBodyElementEnd(this, line, col, start, end);
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTElement.java
new file mode 100644
index 0000000..f64f043
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTElement.java
@@ -0,0 +1,199 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETASTElement.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * An abstract class representing common aspects of all JET AST elements.
+ *
+ * <p>
+ * This class is not intended to be subclassed by clients
+ * </p>
+ */
+public abstract class JETASTElement {
+
+ private final int start;
+
+ private final int end;
+
+ private final JETAST ast;
+
+ private final int line;
+
+ private JETASTElement parent = null;
+
+ private final int column;
+
+ /**
+ * @return Returns the parent.
+ */
+ public final JETASTElement getParent() {
+ return parent;
+ }
+
+ /**
+ * Set the parent element
+ *
+ * @param parent
+ * The parent to set.
+ */
+ final void setParent(JETASTElement parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Construct a new AST element
+ *
+ * @param ast
+ * the AST root
+ * @param line
+ * the line of the element
+ * @param column
+ * the one-based offset within the line of the element's start.
+ * @param start
+ * the start offset of the element
+ * @param end
+ * the end offset of the element
+ *
+ */
+ JETASTElement(JETAST ast, int line, int column, int start, int end) {
+ super();
+ this.ast = ast;
+ this.column = column;
+ this.line = line;
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * The document relative offset of the start of the element.
+ *
+ * @return the start offset
+ */
+ public final int getStart() {
+ return start;
+ }
+
+ /**
+ * The document relative offset of the first character after the element.
+ *
+ * @return the end offset
+ */
+ public final int getEnd() {
+ return end;
+ }
+
+ /**
+ * Visit the AST and its contained elements.
+ *
+ * @param visitor
+ */
+ public final void accept(JETASTVisitor visitor) {
+ visitor.preVisit(this);
+ accept0(visitor);
+ visitor.postVisit(this);
+ }
+
+ /**
+ * Visit the AST and its contained elements.
+ *
+ * @param visitor
+ */
+ protected abstract void accept0(JETASTVisitor visitor);
+
+ /**
+ * Return the AST root object
+ *
+ * @return the AST root object
+ * @since 0.8.0
+ */
+ public JETAST getAst() {
+ return ast;
+ }
+
+ /**
+ * Return the line (one-based) on which the element starts.
+ *
+ * @return the line number.
+ */
+ public final int getLine() {
+ return line;
+ }
+
+ /**
+ * @param element
+ * The element for which body elements are sought
+ * @return the body elements object
+ */
+ private BodyElements getBodyElements(JETASTElement element) {
+ BodyElements bodyElements = null;
+ if (parent instanceof JETCompilationUnit) {
+ bodyElements = ((JETCompilationUnit) parent)
+ .getInternalBodyElements();
+ } else if (parent instanceof XMLBodyElement) {
+ bodyElements = ((XMLBodyElement) parent).getInternalBodyElements();
+ }
+ return bodyElements;
+ }
+
+ public JETASTElement getNextElement() {
+ JETASTElement next = null;
+ if (parent != null) {
+ BodyElements bodyElements = getBodyElements(parent);
+ if (bodyElements != null) {
+ next = bodyElements.elementAfter(this);
+ } else {
+ next = parent.getNextElement();
+ }
+
+ }
+ return next;
+ }
+
+ public JETASTElement getPrevElement() {
+ JETASTElement prev = null;
+ if (parent != null) {
+ BodyElements bodyElements = getBodyElements(parent);
+ if (bodyElements != null) {
+ prev = bodyElements.elementBefore(this);
+ } else {
+ prev = parent.getPrevElement();
+ }
+
+ }
+ return prev;
+ }
+
+ /**
+ * Return the column number (one-based) at which the element starts.
+ *
+ * @return the column number.
+ */
+ public final int getColumn() {
+ return column;
+ }
+
+ /**
+ * Indicate whether the the surrounding whitespace, including the trailing
+ * new line should be removed from the template output. In general, elements
+ * that create output should return <code>false</code>, while element
+ * that do should should return <code>true</code>.
+ *
+ * @return <code>true</code> if the containing line should be removed if
+ * otherwise empty.
+ */
+ public abstract boolean removeLineWhenOtherwiseEmpty();
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTParser.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTParser.java
new file mode 100644
index 0000000..a34bbcb
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTParser.java
@@ -0,0 +1,182 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETASTParser.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.IJETParser;
+import org.eclipse.jet.core.parser.ITagLibraryResolver;
+import org.eclipse.jet.core.parser.ITemplateInput;
+import org.eclipse.jet.core.parser.ITemplateResolver;
+import org.eclipse.jet.core.parser.RecursiveIncludeException;
+import org.eclipse.jet.internal.core.parser.InternalJET1Parser;
+import org.eclipse.jet.internal.core.parser.InternalJET2Parser;
+import org.eclipse.jet.taglib.TagLibrary;
+
+/**
+ * Parser for creating JET ASTs
+ * @since 0.8.0
+ */
+public final class JETASTParser implements IJETParser {
+
+ private static final class NullTagLibraryResolver implements
+ ITagLibraryResolver {
+
+ public TagLibrary getLibrary(String tagLibraryID) {
+ return null;
+ }
+ }
+
+ /**
+ * Builder for JETAST Parser
+ */
+ public static final class Builder {
+
+ private ITemplateResolver templateResolver = null;
+
+ private Map predefinedTagLibraries;
+
+ private ITagLibraryResolver tagLibraryResolver;
+
+ private final int jetSpec;
+
+ /**
+ * Create a JETASTParser builder for a parser using the specified JET language specification
+ * @param jetSpec either {@link JETAST#JET_SPEC_V1} or {@link JETAST#JET_SPEC_V2}.
+ * @throws IllegalArgumentException if <code>jetSpec</code> is not one of the specified values.
+ */
+ public Builder(int jetSpec) {
+ if(jetSpec != JETAST.JET_SPEC_V1 && jetSpec != JETAST.JET_SPEC_V2) {
+ throw new IllegalArgumentException();
+ }
+ this.jetSpec = jetSpec;
+ }
+
+ /**
+ * Specify a template resolver for the parser. If not specified,
+ * then not template paths are resolvable, and all templatePath references
+ * in parser APIs or in JET directives will result in compilation errors.
+ * @param templateResolver a templateResolver. Cannot be <code>null</code>
+ * @return the builder
+ * @throws NullPointerException if <code>templateResolver</code> is <code>null</code>
+ */
+ public Builder templateResolver(ITemplateResolver templateResolver) {
+ if (templateResolver == null) {
+ throw new NullPointerException();
+ }
+ this.templateResolver = templateResolver;
+ return this;
+ }
+
+ /**
+ * Specify a map of predefined JET tag library prefixes to their corresponding JET
+ * tag libary ids. If not specified, then not tag libraries are predefined.
+ * @param predefinedTagLibraries a non-null map of predefined JET tag libraries.
+ * @return the builder.
+ * @throws NullPointerException if <code>predefinedTagLibraries</code> is <code>null</code>
+ */
+ public Builder predefinedTagLibraries(Map predefinedTagLibraries) {
+ if (predefinedTagLibraries == null) {
+ throw new NullPointerException();
+ }
+ this.predefinedTagLibraries = predefinedTagLibraries;
+ return this;
+ }
+
+ /**
+ * Specify a tag library resolver for the parser
+ * @param tagLibraryResolver a tag library resolver instance
+ * @return the tag builder
+ */
+ public Builder tagLibraryResolver(ITagLibraryResolver tagLibraryResolver) {
+ this.tagLibraryResolver = tagLibraryResolver;
+ return this;
+ }
+
+ /**
+ * Build the JETASTParser
+ * @return the new parser.
+ */
+ public JETASTParser build() {
+ return new JETASTParser(this);
+ }
+ }
+
+ private static final ITemplateResolver nullTemplateResolver = new ITemplateResolver() {
+
+ public URI[] getBaseLocations() {
+ URI nullURI;
+ try {
+ nullURI = new URI(""); //$NON-NLS-1$
+ return new URI[] { nullURI };
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public ITemplateInput getIncludedInput(String templatePath,
+ ITemplateInput[] activeInputs) throws RecursiveIncludeException {
+ return null;
+ }
+
+ public ITemplateInput getInput(String templatePath) {
+ return null;
+ }
+
+ };
+
+ private final IJETParser parser;
+
+ /**
+ * Private constructor based on Builder
+ * @param builder
+ */
+ private JETASTParser(Builder builder) {
+ ITemplateResolver templateResolver = builder.templateResolver == null ? nullTemplateResolver
+ : builder.templateResolver;
+ Map predefinedTagLibraries = builder.predefinedTagLibraries == null ? Collections.EMPTY_MAP
+ : builder.predefinedTagLibraries;
+
+
+ if(builder.jetSpec == JETAST.JET_SPEC_V2) {
+ parser = new InternalJET2Parser(templateResolver,
+ builder.tagLibraryResolver == null ?
+ new NullTagLibraryResolver() : builder.tagLibraryResolver,
+ predefinedTagLibraries);
+ } else {
+ parser = new InternalJET1Parser(templateResolver);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.IJETParser#parse(java.lang.String)
+ */
+ public Object parse(String templatePath) {
+ return parser.parse(templatePath);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.parser.IJETParser#parse(char[])
+ */
+ public Object parse(char[] template) {
+ return parser.parse(template);
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTVisitor.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTVisitor.java
new file mode 100644
index 0000000..361f237
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETASTVisitor.java
@@ -0,0 +1,271 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETASTVisitor.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * A visitor for a JETAST tree. This implementation provides 'do nothing' actions for each visit method.
+ * Subclasses need override only the visit methods required.
+ *
+ * @see JETASTVisitor
+ * @since 0.8.0
+ *
+ */
+public abstract class JETASTVisitor {
+
+ /**
+ *
+ */
+ public JETASTVisitor() {
+ super();
+ }
+
+ /**
+ * Visit a JETCompilationUnit element, prior to visiting its body elements.
+ * @param compilationUnit
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(JETCompilationUnit compilationUnit) {
+ return true;
+ }
+
+ /**
+ * Visit a JETCompilationUnit element, after visiting its body elements.
+ * @param compilationUnit
+ */
+ public void endVisit(JETCompilationUnit compilationUnit) {
+ // do nothing
+ }
+
+ /**
+ * Visit a JavaDeclaration.
+ * @param declaration the JavaDeclaration element
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(JavaDeclaration declaration) {
+ return true;
+ }
+
+ /**
+ * Visit a JETDirective.
+ * @param directive the JETDirective element
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(JETDirective directive) {
+ return true;
+ }
+
+ /**
+ * Visit a JavaExpression.
+ * @param expression
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(JavaExpression expression) {
+ return true;
+ }
+
+ /**
+ * Visit a JavaScriptlet.
+ * @param scriptlet
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(JavaScriptlet scriptlet) {
+ return true;
+ }
+
+ /**
+ * Visit a TextElement.
+ * @param text
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(TextElement text) {
+ return true;
+ }
+
+ /**
+ * Visit an XMLEmptyElement.
+ * @param xmlEmptyElement
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(XMLEmptyElement xmlEmptyElement) {
+ return true;
+ }
+
+ /**
+ * Visit an XMLBodyElement, prior to visiting its body elements.
+ * @param xmlBodyElement
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(XMLBodyElement xmlBodyElement) {
+ return true;
+ }
+
+ /**
+ * Visit an XMLBodyElement, after visiting its body elements.
+ * @param xmlBodyElement
+ */
+ public void endVisit(XMLBodyElement xmlBodyElement) {
+ // do nothing
+ }
+
+ /**
+ * Visit the end tag of an XMLBodyElement. Happens after {@link #endVisit(XMLBodyElement)}.
+ * @param xmlBodyElementEnd
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(XMLBodyElementEnd xmlBodyElementEnd) {
+ return true;
+ }
+
+ /**
+ * Visit a Comment element.
+ * @param comment
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(Comment comment) {
+ return true;
+ }
+
+ /**
+ * Visit a section of included content (JET1 only)
+ * @param content the included content element
+ * @return <code>true</code> if the children of this element should be
+ * visited, and <code>false</code> if the children of this element should
+ * be skipped
+ */
+ public boolean visit(IncludedContent content) {
+ return true;
+ }
+
+ /**
+ * Post visit a section of included content (JET1 only)
+ * @param content the included content element
+ */
+ public void endVisit(IncludedContent content) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a JavaDeclaration element, after visiting its children.
+ * @param declaration the JavaDeclaration
+ */
+ public void endVisit(JavaDeclaration declaration) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a JETDirective element, after visiting its children.
+ * @param directive the JETDirective element
+ */
+ public void endVisit(JETDirective directive) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a JavaExpression element, after visiting its children.
+ * @param expression the JavaExpression element
+ */
+ public void endVisit(JavaExpression expression) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a JavaScriptlet element, after visiting its children.
+ * @param scriptlet the JavaScriptlet element
+ */
+ public void endVisit(JavaScriptlet scriptlet) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a TextElement element, after visiting its children.
+ * @param text the TextElement element
+ */
+ public void endVisit(TextElement text) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a XMLEmptyElement element, after visiting its children.
+ * @param xmlEmptyElement the XMLEmptyElement element
+ */
+ public void endVisit(XMLEmptyElement xmlEmptyElement) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a XMLBodyElementEnd element, after visiting its children.
+ * @param xmlBodyElementEnd the XMLBodyElementEnd element
+ */
+ public void endVisit(XMLBodyElementEnd xmlBodyElementEnd) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visit a Comment element, after visiting its children.
+ * @param comment the Comment element
+ */
+ public void endVisit(Comment comment) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visits the AST Node after all type specific visit/end visits
+ * @param element the AST element
+ */
+ public void postVisit(JETASTElement element) {
+ // Do nothing
+ }
+
+
+ /**
+ * Visits an AST Node prior to a type specific visit
+ * @param element the AST element
+ */
+ public void preVisit(JETASTElement element) {
+ // Do nothing
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETCompilationUnit.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETCompilationUnit.java
new file mode 100644
index 0000000..73fead1
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETCompilationUnit.java
@@ -0,0 +1,290 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETCompilationUnit.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jet.core.parser.ProblemSeverity;
+import org.eclipse.jet.taglib.TagLibraryReference;
+
+/**
+ * Represent a compilation unit (a template) in the JET AST.
+ *
+ */
+public final class JETCompilationUnit extends JETASTElement {
+
+ private BodyElements bodyElements = null;
+
+ private final List problems = new ArrayList();
+
+ private String outputJavaPackage = null;
+
+ private String outputJavaClassName;
+
+ private boolean errors = false;
+
+ private boolean warnings = false;
+
+ private TagLibraryReference[] tagLibraryReferences;
+
+ private Set imports = new LinkedHashSet();
+
+ private final URI baseLocation;
+
+ private final String templatePath;
+
+ private final String encoding;
+
+ /**
+ * Create a JET2Compilation Unit
+ *
+ * @param ast
+ * the parent AST
+ * @param baseLocation
+ * the base location URI or <code>null</code>
+ * @param templatePath
+ * the template path or the empty string
+ * @param encoding
+ * the template encoding or <code>null</code>
+ */
+ JETCompilationUnit(JETAST ast, URI baseLocation, String templatePath,
+ String encoding) {
+ super(ast, -1, -1, -1, -1);
+ this.baseLocation = baseLocation;
+ this.templatePath = templatePath;
+ this.encoding = encoding;
+ }
+
+ /**
+ * Define tag library prefixes (and associated tag library ids) that are
+ * automatically available to the transform.
+ *
+ * @param predefinedLibraryMap
+ * a map from prefix to tag library id.
+ */
+ public void setPredefinedTagLibraries(Map predefinedLibraryMap) {
+ }
+
+ /**
+ * Return the internal BodyElements object that gives access to an
+ * updateable list of AST elements the the compilation unit contains
+ *
+ * @return a BodyElements object
+ */
+ BodyElements getInternalBodyElements() {
+ if (bodyElements == null) {
+ bodyElements = new BodyElements(this);
+ }
+ return bodyElements;
+ }
+
+ /**
+ * Return a {@link List} of JET2 AST element (@link JETASTElement}
+ * instances.
+ *
+ * @return a List. The empty list of there are no body elements.
+ */
+ public final List getBodyElements() {
+ if (bodyElements == null) {
+ return Collections.EMPTY_LIST;
+ } else {
+ return bodyElements.getBodyElements();
+ }
+ }
+
+ public final void addBodyElement(BodyElement bodyElement) {
+ getInternalBodyElements().addBodyElement(bodyElement);
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected final void accept0(JETASTVisitor visitor) {
+ final boolean visitChildren = visitor.visit(this);
+ if (visitChildren) {
+ for (Iterator i = getBodyElements().iterator(); i.hasNext();) {
+ JETASTElement element = (JETASTElement) i.next();
+ element.accept0(visitor);
+ }
+ }
+ visitor.endVisit(this);
+ }
+
+ /**
+ * Return a list of problems discovered in the compilation unit
+ *
+ * @return a List of {@link Problem} objects. The empty list is returned if
+ * no problems were found.
+ */
+ public List getProblems() {
+ return Collections.unmodifiableList(problems);
+ }
+
+ /**
+ * Test if the compilation unit has any errors
+ *
+ * @return <code>true</code> if the compilation unit had errors,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasErrors() {
+ return errors;
+ }
+
+ /**
+ * Test if the compilation unit has any warnings
+ *
+ * @return <code>true</code> if the compilation unit had warnings,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasWarnings() {
+ return warnings;
+ }
+
+ /**
+ * Create a new problem on the compilation unit
+ *
+ * @param error
+ * the severity of the problem
+ * @param problemId
+ * the problem id. A value from {@link Problem} static files
+ * @param message
+ * an error message, with optional replacement tokens
+ * @param messageArgs
+ * the error message arguments
+ * @param start
+ * the start offset of the problem (doc relative)
+ * @param end
+ * the end offset of the problem (doc relative)
+ * @param line
+ * the line number of the problem (1 based)
+ * @param colOffset
+ * TODO
+ */
+ public void createProblem(ProblemSeverity error, int problemId,
+ String message, Object[] messageArgs, int start, int end, int line,
+ int colOffset) {
+ if (error == ProblemSeverity.ERROR) {
+ errors = true;
+ } else if (error == ProblemSeverity.WARNING) {
+ warnings = true;
+ }
+ problems.add(new Problem(baseLocation, templatePath, error, problemId,
+ message, messageArgs, start, end, line, colOffset));
+ }
+
+ /**
+ * Return the name of the Java package to which the compilation unit will be
+ * compiled.
+ *
+ * @return a string
+ */
+ public String getOutputJavaPackage() {
+ return outputJavaPackage;
+ }
+
+ /**
+ * Return the unqualified name of the Java class into which the compilation
+ * unit will be compiled.
+ *
+ * @return Returns the outputJavaClassName.
+ */
+ public String getOutputJavaClassName() {
+ return outputJavaClassName;
+ }
+
+ /**
+ * Set the unqualifeid name of the Java class into which the compilation
+ * unit will be compiled.
+ *
+ * @param outputJavaClassName
+ * The outputJavaClassName to set.
+ */
+ public void setOutputJavaClassName(String outputJavaClassName) {
+ this.outputJavaClassName = outputJavaClassName;
+ }
+
+ /**
+ * Set the Java package into which the compilation unit will be compiled.
+ *
+ * @param outputJavaPackage
+ * The outputJavaPackage to set.
+ */
+ public void setOutputJavaPackage(String outputJavaPackage) {
+ this.outputJavaPackage = outputJavaPackage;
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return false;
+ }
+
+ /**
+ * Return an array of tag libraries referenced by this template.
+ *
+ * @return a possibly empty array of tag library references.
+ */
+ public TagLibraryReference[] getTagLibraryReferences() {
+ return tagLibraryReferences == null ? new TagLibraryReference[0]
+ : tagLibraryReferences;
+ }
+
+ public void addImports(List list) {
+ imports.addAll(list);
+ }
+
+ public Set getImports() {
+ return Collections.unmodifiableSet(imports);
+ }
+
+ /**
+ * Set the tag libraries referenced by this template
+ *
+ * @param tagLibraryReferences
+ * the tag library references
+ * @since 0.8.0
+ */
+ public void setTagLibraryReferences(
+ TagLibraryReference[] tagLibraryReferences) {
+ this.tagLibraryReferences = tagLibraryReferences;
+ }
+
+ /**
+ * Return the output encoding for the template
+ *
+ * @return the output encoding
+ * @since 0.8.0
+ */
+ public String getOutputEncoding() {
+ return encoding;
+ }
+
+ public JETASTElement elementAfter(JETASTElement element) {
+ return bodyElements.elementAfter(element);
+ }
+
+ public BodyElement elementBefore(JETASTElement element) {
+ return bodyElements.elementBefore(element);
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETDirective.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETDirective.java
new file mode 100644
index 0000000..cb8761f
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JETDirective.java
@@ -0,0 +1,90 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETDirective.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Define a Directive Element in the JET AST
+ *
+ */
+public final class JETDirective extends BodyElement {
+
+ private final String name;
+
+ private final Map attributes;
+
+ /**
+ * Create an instance
+ *
+ * @param ast
+ * the owning JET2 AST
+ * @param line
+ * the start line of the directive
+ * @param colOffset
+ * the offset within the line of the element's start.
+ * @param start
+ * the start offset of the directive (doc relative)
+ * @param end
+ * the end offset of the directive (doc relative)
+ * @param name
+ * the directive name
+ * @param attributes
+ * a Map of attribute names to attribute values for the directive
+ */
+ JETDirective(JETAST ast, int line, int colOffset, int start, int end,
+ String name, Map attributes) {
+ super(ast, line, colOffset, start, end);
+ this.name = name;
+ this.attributes = Collections.unmodifiableMap(new LinkedHashMap(
+ attributes));
+ }
+
+ /**
+ * Return the directive name
+ *
+ * @return the directive name
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Return a Map the directive attribute names to values
+ *
+ * @return a Map of the directive attributes (unmodifiable)
+ */
+ public final Map getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaDeclaration.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaDeclaration.java
new file mode 100644
index 0000000..1a70a0d
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaDeclaration.java
@@ -0,0 +1,54 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JavaDeclaration.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * Define a Java Declaration Element in the JET AST
+ *
+ */
+public final class JavaDeclaration extends JavaElement {
+
+ /**
+ * Create an instance
+ * @param ast the owning AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param javaStart the start offset of the Java code (doc relative)
+ * @param javaEnd the end offset of the Java code (doc relative)
+ * @param javaContent the Java code
+ */
+ JavaDeclaration(JETAST ast, int line, int colOffset, int start, int end,
+ int javaStart, int javaEnd, char[] javaContent) {
+ super(ast, line, colOffset, start, end, javaStart, javaEnd, javaContent);
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected final void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaElement.java
new file mode 100644
index 0000000..226f2c3
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaElement.java
@@ -0,0 +1,77 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JavaElement.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * Abstract representation of JET AST elements that contain Java code
+ *
+ * <p>
+ * This class is not intended to be subclassed by clients
+ * </p>
+ */
+public abstract class JavaElement extends BodyElement {
+
+ private final int javaStart;
+
+ private final int javaEnd;
+
+ private final char[] javaContent;
+
+ /**
+ * Create a new instance
+ * @param ast the root AST to which the element belongs
+ * @param line the line in which the element begins
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the offset at which the element starts (doc relative)
+ * @param end the offset at which the element ends (doc relative)
+ * @param javaStart the offset at which the Java code starts (doc relative)
+ * @param javaEnd the offset at which the Java code ends (doc relative)
+ * @param javaContent the java code
+ */
+ JavaElement(JETAST ast, int line, int colOffset, int start, int end,
+ int javaStart, int javaEnd, char[] javaContent) {
+ super(ast, line, colOffset, start, end);
+ this.javaStart = javaStart;
+ this.javaEnd = javaEnd;
+ this.javaContent = javaContent;
+ }
+
+ /**
+ * The document relative offset of the Java code within the element.
+ * @return the start offset
+ */
+ public final int getJavaStart() {
+ return javaStart;
+ }
+
+ /**
+ * The document relative offset of the first character after the Java code.
+ * @return the end offset
+ */
+ public final int getJavaEnd() {
+ return javaEnd;
+ }
+
+ /**
+ * Return the Java content of the element
+ * @return the Java content
+ */
+ public String getJavaContent() {
+ return new String(javaContent);
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaExpression.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaExpression.java
new file mode 100644
index 0000000..40e4085
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaExpression.java
@@ -0,0 +1,55 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JavaExpression.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * Define a Java Expression Element in the JET AST
+ *
+ */
+
+public final class JavaExpression extends JavaElement {
+
+ /**
+ * Create an instance
+ * @param ast the owning AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param javaStart the start offset of the Java code (doc relative)
+ * @param javaEnd the end offset of the Java code (doc relative)
+ * @param javaContent the Java code
+ */
+ JavaExpression(JETAST ast, int line, int colOffset, int start, int end,
+ int javaStart, int javaEnd, char[] javaContent) {
+ super(ast, line, colOffset, start, end, javaStart, javaEnd, javaContent);
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return false;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaScriptlet.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaScriptlet.java
new file mode 100644
index 0000000..07bb580
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/JavaScriptlet.java
@@ -0,0 +1,63 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JavaScriptlet.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+/**
+ * Define a Java Scriptlet Element in the JET AST
+ *
+ */
+public final class JavaScriptlet extends JavaElement {
+
+ /**
+ * Create an instance
+ *
+ * @param ast
+ * the owning AST
+ * @param line
+ * the start line of the element
+ * @param colOffset
+ * the offset within the line of the element's start.
+ * @param start
+ * the start offset of the element (doc relative)
+ * @param end
+ * the end offset of the element (doc relative)
+ * @param javaStart
+ * the start offset of the Java code (doc relative)
+ * @param javaEnd
+ * the end offset of the Java code (doc relative)
+ * @param javaContent
+ * the Java code
+ */
+ JavaScriptlet(JETAST ast, int line, int colOffset, int start, int end,
+ int javaStart, int javaEnd, char[] javaContent) {
+ super(ast, line, colOffset, start, end, javaStart, javaEnd, javaContent);
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Problem.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Problem.java
new file mode 100644
index 0000000..10a9f96
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/Problem.java
@@ -0,0 +1,258 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: Problem.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+import java.net.URI;
+import java.text.MessageFormat;
+
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+
+/**
+ * Represent a compilation problem on a JET2 tempalte.
+ * <p>
+ * This class is not intended to be extended by clients
+ * </p>
+ * @since 0.8.0
+ */
+public final class Problem {
+
+ /**
+ * Error Id for an XML end tag that has no corresponding start tag.
+ * @see #getId()
+ * @deprecated Use {@link IProblem#MissingXmlStartTag}
+ */
+ public static final int MissingXmlStartTag = IProblem.MissingXmlStartTag;
+
+ /**
+ * Error Id for an XML start tag that has no corresponding end tag.
+ * @see #getId()
+ * @deprecated Use {@link IProblem#MissingXmlEndTag}
+ */
+ public static final int MissingXmlEndTag = IProblem.MissingXmlEndTag;
+
+ /**
+ * Error Id for an XML tag or JET directive that is missing a required attribute
+ * @see #getId()
+ * @deprecated Use {@link IProblem#MissingRequiredAttribute}
+ */
+ public static final int MissingRequiredAttribute = IProblem.MissingRequiredAttribute;
+
+ /**
+ * Error Id for taglib directive that defines a prefix defined by a preceding taglib directive
+ * @see #getId()
+ * @deprecated Use {@link IProblem#DuplicateXMLNamespacePrefix}
+ */
+ public static final int DuplicateXMLNamespacePrefix = IProblem.DuplicateXMLNamespacePrefix;
+
+ /**
+ * Error Id for taglib directive that defines references an unknown tag library id
+ * @see #getId()
+ * @deprecated Use {@link IProblem#UnknownTagLibrary}
+ */
+ public static final int UnknownTagLibrary = IProblem.UnknownTagLibrary;
+
+ /**
+ * Error Id for an attribute that is not defined in the tag definition
+ * @deprecated Use {@link IProblem#UnknownAttributeInTag}
+ */
+ public static final int UnknownAttributeInTag = IProblem.UnknownAttributeInTag;
+
+ /**
+ * Represent an unterminated XML Tag
+ * @deprecated Use {@link IProblem#UnterminatedXMLTag}
+ */
+ public static final int UnterminatedXMLTag = IProblem.UnterminatedXMLTag;
+
+ /**
+ * Represent a duplicate attribute in an XML Tag;
+ * @deprecated Use {@link IProblem#DuplicateAttribute}
+ */
+ public static final int DuplicateAttribute = IProblem.DuplicateAttribute;
+
+ /**
+ * An underlying JETException was thrown by the JET parser
+ * @deprecated Use {@link IProblem#JETException}
+ */
+ public static final int JETException = IProblem.JETException;
+
+ /**
+ * Two templates specify that they compile to the same Java Class
+ * @deprecated Use {@link IProblem#MultipleTemplatesWithSameJavaClass}
+ */
+ public static final int MultipleTemplatesWithSameJavaClass = IProblem.MultipleTemplatesWithSameJavaClass;
+
+ /**
+ * Use of an attribute that has been deprecated.
+ * @deprecated Use {@link IProblem#DeprecatedAttribute}
+ */
+ public static final int DeprecatedAttribute = IProblem.DeprecatedAttribute;
+
+ /**
+ * Tag may not have a body - the tag must be of the form &lt;tagName/&gt;.
+ * @deprecated Use {@link IProblem#TagCannotHaveContent}
+ */
+ public static final int TagCannotHaveContent = IProblem.TagCannotHaveContent;
+
+ /**
+ * Tag must have content - the tag must be of the form &lt;tagName&gt;xxx&lt;/tagName&gt;.
+ * @deprecated Use {@link IProblem#TagCannotBeEmpty}
+ */
+ public static final int TagCannotBeEmpty = IProblem.TagCannotBeEmpty;
+
+ /**
+ * Use of the tag has been deprecated.
+ * @deprecated Use {@link IProblem#DeprecatedTag}
+ */
+ public static final int DeprecatedTag = IProblem.DeprecatedTag;
+
+ /**
+ * Unsupported Directive.
+ * @deprecated Use {@link IProblem#UnsupportedDirective}
+ */
+ public static final int UnsupportedDirective = IProblem.UnsupportedDirective;
+
+ /**
+ * A tag that has a known tag library prefix, but is not a recognized name. Usually
+ * indicates a typographical error.
+ * @deprecated Use {@link IProblem#UnknownXMLTag}
+ */
+ public static final int UnknownXMLTag = IProblem.UnknownXMLTag;
+
+ /**
+ * A tag this is declared as an 'emptyTag' occured as &lt;tag ...&gt;, and has been
+ * interpreted as the equivalent empty tag &lt;tag .../&gt;.
+ * @deprecated Use {@link IProblem#TagInterpretedAsEmptyTag}
+ */
+ public static final int TagInterpretedAsEmptyTag = IProblem.TagInterpretedAsEmptyTag;
+
+ private final String originatingFileName;
+
+ private final int id;
+
+ private final String message;
+
+ private final Object[] messageArgs;
+
+ private final int start;
+
+ private final int end;
+
+ private final int lineNumber;
+
+ private final ProblemSeverity problemSeverity;
+
+ private final int colOffset;
+
+ /**
+ * Create an new instance
+ * @param baseLocation the base location from which the template path is resolved.
+ * @param templatePath the file name from which the problem originates
+ * @param problemSeverity the problemSeverity
+ * @param id the problem id. See static constants declared on this class
+ * @param message the error message in {@link MessageFormat} style
+ * @param messageArgs the error message arguments
+ * @param start the start offset of the problem (doc relative)
+ * @param end the end offset of the problem (doc relative)
+ * @param lineNumber the start line of the problem
+ * @param colOffset TODO
+ */
+ public Problem(URI baseLocation, String templatePath,
+ ProblemSeverity severity, int id, String message,
+ Object[] messageArgs, int start, int end, int lineNumber,
+ int colOffset) {
+ super();
+ this.originatingFileName = templatePath;
+ this.problemSeverity = severity;
+ this.id = id;
+ this.colOffset = colOffset;
+ this.message = messageArgs != null && messageArgs.length > 0 ? MessageFormat
+ .format(message, messageArgs)
+ : message;
+ this.messageArgs = messageArgs;
+ this.start = start;
+ this.end = end;
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * @return Returns the end.
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * @return Returns the id.
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * @return Returns the lineNumber.
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * @return Returns the message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * @return Returns the messageArgs.
+ */
+ public Object[] getMessageArgs() {
+ return messageArgs;
+ }
+
+ /**
+ * @return Returns the originatingFileName.
+ */
+ public String getOriginatingFileName() {
+ return originatingFileName;
+ }
+
+ /**
+ * @return Returns the start.
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * @return Returns the colOffset.
+ */
+ public final int getColOffset() {
+ return colOffset;
+ }
+
+ /**
+ * Return the problem severity
+ * @return the problem severity
+ * @since 0.8.0
+ */
+ public ProblemSeverity getProblemSeverity() {
+ return problemSeverity;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TagLibraryUsageManager.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TagLibraryUsageManager.java
new file mode 100644
index 0000000..1b5d93f
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TagLibraryUsageManager.java
@@ -0,0 +1,213 @@
+/**
+ *
+ */
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.ITagLibraryResolver;
+import org.eclipse.jet.internal.taglib.TagLibraryReferenceImpl;
+import org.eclipse.jet.taglib.TagDefinition;
+import org.eclipse.jet.taglib.TagLibrary;
+import org.eclipse.jet.taglib.TagLibraryReference;
+
+/**
+ * A class to manage usage of tag libraries in a JET2 template.
+ *
+ */
+public class TagLibraryUsageManager {
+
+ private final Map tagLibraries = new HashMap();
+
+ private final Map predefinedLibraryMap;
+
+ private List tags = null;
+
+ private final ITagLibraryResolver tagLibraryResolver;
+
+ /**
+ * @param predefinedLibraryMap
+ * a map of predefined prefixes and their tag libary ids.
+ *
+ */
+ public TagLibraryUsageManager(Map predefinedLibraryMap,
+ ITagLibraryResolver tagLibraryResolver) {
+ this.tagLibraryResolver = tagLibraryResolver;
+ // make a defensive copy, we may change it...
+ this.predefinedLibraryMap = new HashMap(predefinedLibraryMap);
+ }
+
+ /**
+ * Test whether a prefix can be created.
+ * <p>
+ * Conditions where this can succeed: <bl>
+ * <li>prefix is not defined in either the predefined library map or the
+ * library map.</li>
+ * </bl>
+ * </p>
+ *
+ * @param prefix
+ * a prefix
+ * @param id
+ * the tag libary id.
+ * @return <code>true</code> if the prefix can be created.
+ */
+ public boolean canDefinePrefix(String prefix, String id) {
+
+ if (!tagLibraries.containsKey(prefix)
+ && !predefinedLibraryMap.containsKey(prefix)) {
+ return true;
+ }
+
+ // check for duplicate definitions of predefined libraries, this is ok,
+ // too.
+ if (id.equals(predefinedLibraryMap.get(prefix))) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isLibraryDefined(String libaryId) {
+ return tagLibraries.containsValue(libaryId);
+ }
+
+ public String getLibraryIdFromPrefix(String prefix) {
+ String id = (String) tagLibraries.get(prefix);
+ if (id == null) {
+ id = (String) predefinedLibraryMap.get(prefix);
+ }
+ return id;
+ }
+
+ /**
+ * Defined a prefix for a tag library id. If a prefix for the tag library id
+ * is already defined in the predefined librarys map, then that prefix is
+ * removed.
+ *
+ * @param prefix
+ * a prefix
+ * @param libraryId
+ * a library id
+ */
+ public void add(String prefix, String libraryId) {
+ tagLibraries.put(prefix, libraryId);
+
+ for (Iterator i = predefinedLibraryMap.entrySet().iterator(); i
+ .hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ if (libraryId.equals(entry.getValue())) {
+ i.remove();
+ }
+ }
+
+ tags = null; // clear the tags list cache
+ }
+
+ /**
+ * Return the TagDefinition of the selected tag.
+ *
+ * @param tagName
+ * the QName of the tag
+ * @return the TagDefinition
+ */
+ public TagDefinition getTagDefinition(String tagName) {
+ int sepIndex = tagName.indexOf(':');
+ String prefix = sepIndex == -1 ? "" : tagName.substring(0, sepIndex); //$NON-NLS-1$
+ String tagNCName = sepIndex == -1 ? tagName : tagName
+ .substring(sepIndex + 1);
+ String id = getLibraryIdFromPrefix(prefix.toLowerCase());
+ TagLibrary tagLibrary = tagLibraryResolver.getLibrary(id);
+ return tagLibrary.getTagDefinition(tagNCName);
+ }
+
+ /**
+ * Test whether the tag name passed is know to this manager
+ *
+ * @return <code>true</code> if the tag matches one of the libraries,
+ * <code>false</code> otherwise.
+ */
+ public boolean isKnownTag(String tagName) {
+ boolean knownTag = false;
+
+ int sepIndex = tagName.indexOf(':');
+ String prefix = sepIndex == -1 ? "" : tagName.substring(0, sepIndex); //$NON-NLS-1$
+ String tagNCName = sepIndex == -1 ? tagName : tagName
+ .substring(sepIndex + 1);
+ String id = getLibraryIdFromPrefix(prefix.toLowerCase());
+ if (id != null) {
+ TagLibrary tagLibrary = tagLibraryResolver.getLibrary(id);
+ if (tagLibrary != null) {
+ knownTag = tagLibrary.hasTag(tagNCName);
+ }
+ }
+ return knownTag;
+ }
+
+ /**
+ * Test whether the tag name passed is known to be an invalid name by the
+ * manager. The tag name is known to be invalid if it has a known prefix,
+ * but an unknown unqualified tag name.
+ *
+ * @return <code>true</code> if the tag matches one of the libraries,
+ * <code>false</code> otherwise.
+ */
+ public boolean isKnownInvalidTagName(String tagName) {
+ boolean knownInvalidTagName = false;
+
+ int sepIndex = tagName.indexOf(':');
+ String prefix = sepIndex == -1 ? "" : tagName.substring(0, sepIndex); //$NON-NLS-1$
+ String tagNCName = sepIndex == -1 ? tagName : tagName
+ .substring(sepIndex + 1);
+ String id = getLibraryIdFromPrefix(prefix.toLowerCase());
+ if (id != null) {
+ TagLibrary tagLibrary = tagLibraryResolver.getLibrary(id);
+ if (tagLibrary != null && prefix.length() > 0) {
+ knownInvalidTagName = !tagLibrary.hasTag(tagNCName);
+ }
+ }
+ return knownInvalidTagName;
+
+ }
+
+ public TagLibraryReference[] getTagLibraryReferences() {
+ List result = new ArrayList(predefinedLibraryMap.size()
+ + tagLibraries.size());
+ for (Iterator i = predefinedLibraryMap.entrySet().iterator(); i
+ .hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ result.add(new TagLibraryReferenceImpl((String) entry.getKey(),
+ (String) entry.getValue(), true));
+ }
+ for (Iterator i = tagLibraries.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ result.add(new TagLibraryReferenceImpl((String) entry.getKey(),
+ (String) entry.getValue(), false));
+ }
+ return (TagLibraryReference[]) result
+ .toArray(new TagLibraryReference[result.size()]);
+ }
+
+ public String[] getKnownTagNames() {
+ List localTags = tags; // protect against concurrent access
+ if (localTags == null) {
+ localTags = new ArrayList();
+ for (Iterator i = tagLibraries.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String prefix = (String) entry.getKey();
+ String id = (String) entry.getValue();
+ TagLibrary tagLibrary = tagLibraryResolver.getLibrary(id);
+ final String[] tagNames = tagLibrary.getTagNames();
+
+ for (int j = 0; j < tagNames.length; j++) {
+ localTags.add(prefix + "." + tagNames[j]); //$NON-NLS-1$
+ }
+ }
+ }
+ return (String[]) localTags.toArray(new String[localTags.size()]);
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TextElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TextElement.java
new file mode 100644
index 0000000..04f1215
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/TextElement.java
@@ -0,0 +1,142 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TextElement.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+import org.eclipse.jet.internal.core.parser.LineInfo;
+
+/**
+ * Define a Text Element in the JET AST
+ *
+ * @since 0.8.0
+ *
+ */
+public final class TextElement extends BodyElement {
+
+ private static final String ESCAPED_JET_ELEMENT_START = "<\\%"; //$NON-NLS-1$
+
+ private char[] text;
+
+ private boolean trimLastLine = false;
+
+ private boolean trimFirstLine = false;
+
+ private final LineInfo[] lines;
+
+ /**
+ * Create an instance
+ * @param jetast the AST
+ * @param text the text
+ */
+ TextElement(JETAST jetast, char[] text) {
+ super(jetast, -1, -1, 0, 0);
+ this.text = handleEscapes(text);
+ this.lines = LineInfo.calculateLines(this.text);
+ }
+
+ /**
+ * Remove any escape sequences in the raw text.
+ * The only escaped sequences handled are:
+ * <bl>
+ * <li>&lt;\% to &lt;%</li>
+ * </bl>
+ * @param originalText
+ * @return
+ */
+ private char[] handleEscapes(char[] originalText) {
+ StringBuffer buffer = new StringBuffer(originalText.length);
+ buffer.append(originalText);
+ for (int i = buffer.indexOf(ESCAPED_JET_ELEMENT_START); i != -1; i = buffer
+ .indexOf(ESCAPED_JET_ELEMENT_START, i)) {
+ buffer.replace(i, i + ESCAPED_JET_ELEMENT_START.length(), "<%"); //$NON-NLS-1$
+ }
+ return buffer.toString().toCharArray();
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ /**
+ * Return the text content
+ * @return the text
+ */
+ public char[] getText() {
+ char[] result = text;
+ if (lines.length > 0) {
+ int start = isTrimFirstLine() && lines[0].hasDelimiter() ? lines[0]
+ .getEnd()
+ + lines[0].getDelimiter().length() : 0;
+ int end = isTrimLastLine()
+ && !lines[lines.length - 1].hasDelimiter() ? lines[lines.length - 1]
+ .getStart()
+ : text.length;
+ result = new String(text, start, end - start).toCharArray();
+ }
+ return result;
+ }
+
+ public char[] getRawText() {
+ return text;
+ }
+
+ /**
+ *
+ * @param newText
+ * @deprecated
+ */
+ void setText(char[] newText) {
+ text = newText;
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return false;
+ }
+
+ public void setTrimLastLine(boolean trim) {
+ this.trimLastLine = trim;
+ }
+
+ /**
+ * @return Returns the trimLastLine.
+ */
+ public final boolean isTrimLastLine() {
+ return trimLastLine;
+ }
+
+ public void setTrimFirstLine(boolean trim) {
+ this.trimFirstLine = trim;
+ }
+
+ /**
+ * @return Returns the trimFirstLine.
+ */
+ public final boolean isTrimFirstLine() {
+ return trimFirstLine;
+ }
+
+ /**
+ * @return Returns the lines.
+ */
+ public final LineInfo[] getLines() {
+ return lines;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElement.java
new file mode 100644
index 0000000..f2d8081
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElement.java
@@ -0,0 +1,142 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: XMLBodyElement.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jet.taglib.TagDefinition;
+
+/**
+ * Define a JET XML Element that has a begin-tag and end-tag, and zero or more
+ * body elements.
+ *
+ * @since 0.8.0
+ */
+public final class XMLBodyElement extends XMLElement {
+
+ private BodyElements bodyElements = null;
+
+ private XMLBodyElementEnd endTag = null;
+
+ /**
+ * Create an instance
+ *
+ * @param ast
+ * the owning AST
+ * @param line
+ * the start line of the element
+ * @param colOffset
+ * the offset within the line of the element's start.
+ * @param start
+ * the start offset of the element (doc relative)
+ * @param end
+ * the end offset of the element (doc relative)
+ * @param name
+ * the QName of the element
+ * @param attributes
+ * a Map of attribute names to their values of the element
+ * @param td
+ * the TagDefinition
+ */
+ XMLBodyElement(JETAST ast, int line, int colOffset, int start, int end,
+ String name, Map attributes, TagDefinition td) {
+ super(ast, line, colOffset, start, end, name, attributes, td);
+ }
+
+ /**
+ * Return a object that allows writable access to the JET2 elements
+ * contained by this element.
+ *
+ * @return a BodyElements instance
+ */
+ BodyElements getInternalBodyElements() {
+ if (bodyElements == null) {
+ bodyElements = new BodyElements(this);
+ }
+ return bodyElements;
+ }
+
+ /**
+ * Return a read-only list of JET2 elements contained by this element.
+ *
+ * @return a List of {@link JETASTElement} instances. The empty list is
+ * returned if there are no elements.
+ */
+ public final List getBodyElements() {
+ if (bodyElements == null) {
+ return Collections.EMPTY_LIST;
+ } else {
+ return bodyElements.getBodyElements();
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected final void accept0(JETASTVisitor visitor) {
+ final boolean visitChildren = visitor.visit(this);
+ if (visitChildren) {
+ for (Iterator i = getBodyElements().iterator(); i.hasNext();) {
+ JETASTElement element = (JETASTElement) i.next();
+ element.accept0(visitor);
+ }
+ }
+ visitor.endVisit(this);
+
+ }
+
+ public void setEndTag(XMLBodyElementEnd endTag) {
+ this.endTag = endTag;
+ }
+
+ /**
+ * @return Returns the endTag.
+ */
+ public final XMLBodyElementEnd getEndTag() {
+ return endTag;
+ }
+
+ public String toString() {
+ return "Line " + getLine() + ": <" + getName() + ">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ public JETASTElement getNextElement() {
+
+ if (getBodyElements().size() > 0) {
+ return (JETASTElement) getBodyElements().get(0);
+ } else {
+ return super.getNextElement();
+ }
+ }
+
+ public void addBodyElement(BodyElement bodyElement) {
+ getInternalBodyElements().addBodyElement(bodyElement);
+
+ }
+
+ public JETASTElement elementAfter(JETASTElement element) {
+ return bodyElements.elementAfter(element);
+ }
+
+ public BodyElement elementBefore(JETASTElement element) {
+ return bodyElements.elementBefore(element);
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElementEnd.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElementEnd.java
new file mode 100644
index 0000000..d21ba62
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLBodyElementEnd.java
@@ -0,0 +1,82 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: XMLBodyElementEnd.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.List;
+
+/**
+ * Represent the closing tag of an XML Tag with a body.
+ *
+ * @since 0.8.0
+ */
+public class XMLBodyElementEnd extends BodyElement {
+
+ private XMLBodyElement startTag;
+
+ /**
+ *
+ * @param ast
+ * @param line
+ * @param colOffset
+ * @param start
+ * @param end
+ * @param startTag
+ */
+ XMLBodyElementEnd(JETAST ast, int line, int colOffset, int start, int end) {
+ super(ast, line, colOffset, start, end);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.compiler.JET2ASTElement#accept(org.eclipse.jet.compiler.JET2ASTVisitor)
+ */
+ protected void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ /**
+ * @return Returns the startTag.
+ */
+ public final XMLBodyElement getStartTag() {
+ return startTag;
+ }
+
+ public boolean removeLineWhenOtherwiseEmpty() {
+ return startTag == null ? false : startTag
+ .removeLineWhenOtherwiseEmpty();
+ }
+
+ /**
+ * @param startTag The startTag to set.
+ */
+ public final void setStartTag(XMLBodyElement startTag) {
+ this.startTag = startTag;
+ }
+
+ public JETASTElement getPrevElement() {
+ final List bodyElements = startTag.getBodyElements();
+ if (bodyElements.size() > 0) {
+ return (JETASTElement) bodyElements.get(bodyElements.size() - 1);
+ } else {
+ return startTag;
+ }
+ }
+
+ public String toString() {
+ return "Line " + getLine() + ": </" + getStartTag().getName() + ">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLElement.java
new file mode 100644
index 0000000..6f37c5c
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLElement.java
@@ -0,0 +1,124 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: XMLElement.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jet.taglib.TagDefinition;
+
+/**
+ * An abstract implementation representing all XML-based elements in the JET AST
+ *
+ * <p>
+ * This class is not intended to be subclassed by clients
+ * </p>
+ *
+ * @since 0.8.0
+ */
+public abstract class XMLElement extends BodyElement {
+
+ private final String name;
+
+ private final Map attributes;
+
+ private final TagDefinition tagDefinition;
+
+ /**
+ * Create an instance
+ *
+ * @param ast
+ * the owning AST
+ * @param line
+ * the start line of the element
+ * @param colOffset
+ * the offset within the line of the element's start.
+ * @param start
+ * the start offset of the element (doc relative)
+ * @param end
+ * the end offset of the element (doc relative)
+ * @param name
+ * the QName of the element
+ * @param attributes
+ * a Map of attribute names to values for the element.
+ * @param td
+ * the TagDefinition
+ */
+ XMLElement(JETAST ast, int line, int colOffset, int start, int end,
+ String name, Map attributes, TagDefinition td) {
+ super(ast, line, colOffset, start, end);
+ this.name = name;
+ this.tagDefinition = td;
+ this.attributes = Collections.unmodifiableMap(new HashMap(attributes)); // defensive
+ // copy
+
+ }
+
+ /**
+ * Return the QName of the element
+ *
+ * @return a string
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * Return a read-only map of the attributes (name to value map)
+ *
+ * @return a Map with String keys (attribute name) and String values
+ */
+ public final Map getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Return the NCName (unqualified name) of the element.
+ *
+ * @return the name with any XML namespace prefix (<i>prefix</i>:) removed
+ */
+ public String getTagNCName() {
+ String qName = getName();
+ int sepIndex = qName.indexOf(':');
+ return sepIndex >= 0 ? qName.substring(sepIndex + 1) : qName;
+ }
+
+ /**
+ * Return the XML Namespace prefixe of the element
+ *
+ * @return the namespace prefix or the empty string if there is no namespace
+ * prefix.
+ */
+ public String getNSPrefix() {
+ String qName = getName();
+ int sepIndex = qName.indexOf(':');
+ return sepIndex >= 0 ? qName.substring(0, sepIndex) : ""; //$NON-NLS-1$
+ }
+
+ /**
+ * @return Returns the td.
+ */
+ public final TagDefinition getTagDefinition() {
+ return tagDefinition;
+ }
+
+ public final boolean removeLineWhenOtherwiseEmpty() {
+ return tagDefinition.removeWhenContainingLineIsEmpty();
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLEmptyElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLEmptyElement.java
new file mode 100644
index 0000000..d53220c
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/core/parser/ast/XMLEmptyElement.java
@@ -0,0 +1,59 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: XMLEmptyElement.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet.core.parser.ast;
+
+import java.util.Map;
+
+import org.eclipse.jet.taglib.TagDefinition;
+
+/**
+ * Define an empty XML element in the JET AST.
+ *
+ * @since 0.8.0
+ */
+public final class XMLEmptyElement extends XMLElement {
+
+ /**
+ * Create an instance
+ * @param ast the owning AST
+ * @param line the start line of the element
+ * @param colOffset the offset within the line of the element's start.
+ * @param start the start offset of the element (doc relative)
+ * @param end the end offset of the element (doc relative)
+ * @param name the QName of the element
+ * @param attributes a Map of attribute names to values for the element.
+ * @param td the TagDefinition
+ */
+ XMLEmptyElement(JETAST ast, int line, int colOffset, int start, int end,
+ String name, Map attributes, TagDefinition td) {
+ super(ast, line, colOffset, start, end, name, attributes, td);
+ }
+
+ /**
+ * @see org.eclipse.jet.compiler.JETASTElement#accept0(JETASTVisitor)
+ */
+ protected void accept0(JETASTVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ public String toString() {
+ return "Line " + getLine() + ": <" + getName() + "/>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/NewLineUtil.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/NewLineUtil.java
new file mode 100644
index 0000000..cf684b5
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/NewLineUtil.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 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.jet.internal.core;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility class for setting new line characters in strings
+ */
+public class NewLineUtil {
+
+ /**
+ * This utility knows about the following line delimiters:
+ * \r\n - windows
+ * \n - linux/unix
+ * \r - Mac OS 9
+ */
+ public static final Pattern NEW_LINE_PATTERN = Pattern.compile("(\\r\\n|\\n|\\r)", Pattern.MULTILINE); //$NON-NLS-1$
+
+ private NewLineUtil() {
+ // do nothing
+ }
+
+ /**
+ * Set the new line in sourceText to the terminator supplied
+ * @param sourceText the source text
+ * @param lineTerminator the terminator to use
+ * @return the text with the supplied line terminator
+ */
+ public static String setLineTerminator(CharSequence sourceText, String lineTerminator) {
+ final Matcher m = NEW_LINE_PATTERN.matcher(sourceText);
+ return m.replaceAll(lineTerminator);
+ }
+
+ /**
+ * Find the line terminator used by the passed text. The line terminator
+ * is the first valid line terminator found. If no line terminator is found then null is return.
+ * @param sourceText the source text
+ * @return the line terminator or <code>null</code> if no line terminator was found.
+ */
+ public static String getLineTerminator(CharSequence sourceText) {
+ final Matcher m = NEW_LINE_PATTERN.matcher(sourceText);
+ return m.find() ? sourceText.subSequence(m.start(), m.end()).toString() : null;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/DuplicateGeneratedClassException.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/DuplicateGeneratedClassException.java
new file mode 100644
index 0000000..7f35478
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/DuplicateGeneratedClassException.java
@@ -0,0 +1,75 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: DuplicateGeneratedClassException.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.compiler;
+
+/**
+ * Describe an exception where a JET template explicitly specifies the same
+ * fully qualified Java class name as another JET template
+ */
+public class DuplicateGeneratedClassException extends Exception
+{
+
+ private final String templatePath;
+ private final String otherTemplatePath;
+ private final String fullyQualifiedJavaClassName;
+
+ /**
+ * Create a DuplicateGeneratedClassException
+ * @param templatePath the templatePath that specifies a common generated Java class.
+ * @param otherTemplatePath the other template Path that previously declared the
+ * @param fullyQualifiedJavaClassName the common fully qualified Java class name specified by the templates
+ */
+ public DuplicateGeneratedClassException(String templatePath, String otherTemplatePath, String fullyQualifiedJavaClassName)
+ {
+ this.templatePath = templatePath;
+ this.otherTemplatePath = otherTemplatePath;
+ this.fullyQualifiedJavaClassName = fullyQualifiedJavaClassName;
+ }
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8154326381507911174L;
+
+ /**
+ * Return the fully qualifed Java class name specified by both templates
+ * @return the fully qualified Java class name
+ */
+ public String getFullyQualifiedJavaClassName()
+ {
+ return fullyQualifiedJavaClassName;
+ }
+
+ /**
+ * Return the template path of the template already specifying the common generated Java class.
+ * @return a template path
+ */
+ public String getOtherTemplatePath()
+ {
+ return otherTemplatePath;
+ }
+
+ /**
+ * Return the template path of a template being compiled when the exception was detected.
+ * @return a template path
+ */
+ public String getTemplatePath()
+ {
+ return templatePath;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/ICompilerOutput.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/ICompilerOutput.java
new file mode 100644
index 0000000..cc5c6dd
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/ICompilerOutput.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 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.jet.internal.core.compiler;
+
+import java.util.List;
+
+/**
+ * Protocol for compiler output.
+ * @see IJETCompiler
+ */
+public interface ICompilerOutput {
+
+ /**
+ * Notification that templatePath is about to be compiled
+ * @param templatePath a templatePath
+ */
+ void preCompile(String templatePath);
+
+ /**
+ * Write generated ouptut with the given encoding
+ * @param outputFilePath a compiler output base location relative path of the file to write
+ * @param contents the file contents
+ * @param encoding the encoding for the generated class. A value of <code>null</code> indicates
+ * the default encoding
+ */
+ void writeOutput(String outputFilePath, String contents, String encoding);
+
+ /**
+ * Remove a generated ouptput file
+ * @param outputFilePath a compiler output base location relative path
+ */
+ void removeOutput(String outputFilePath);
+
+ /**
+ * Record problems found in the given templatePath
+ * @param templatePath the templatePath being compiled
+ * @param problems a list of problems
+ */
+ void recordProblems(String templatePath, List problems);
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IJETCompiler.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IJETCompiler.java
new file mode 100644
index 0000000..d079879
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IJETCompiler.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 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.jet.internal.core.compiler;
+
+import java.io.Serializable;
+
+
+/**
+ * Protocol for an incremental JET Compiler. Typically, the compiler methods are called in the following order
+ * <ol>
+ * <li>{@link #clean()} - optional - remove any previously created compiler output</li>
+ * <li>{@link #compile(String)} or {@link #removeOutput(String)} - once for each tempalte to compiler or template removed, respectively</li>
+ * <li>{@link #finish()} - once, after all templates are compiled</li>
+ * <li>{@link #getTagLibaryDependencies()} - once, in order to track tag library dependiencies
+ * <li>{@link #getMemento()} - optional - obtain the compiler state for potential re-use later
+ * </ol>
+ *
+ */
+public interface IJETCompiler {
+
+ /**
+ * Enumeration defining results for {@link IJETCompiler#compile(String)}
+ *
+ */
+ public static final class CompileResult {
+
+ /**
+ * Compile was successful with no errors or warnings
+ */
+ public static final CompileResult OK = new CompileResult("OK"); //$NON-NLS-1$
+
+ /**
+ * Compile was successful with warnings
+ */
+ public static final CompileResult WARNINGS = new CompileResult("WARNINGS"); //$NON-NLS-1$
+
+ /**
+ * Compile failed with errors or errors and warnings
+ */
+ public static final CompileResult ERRORS = new CompileResult("ERRORS"); //$NON-NLS-1$
+
+ /**
+ * Compile was not attempted - file did not have a recognized file extension
+ */
+ public static final CompileResult IGNORED = new CompileResult("IGNORED"); //$NON-NLS-1$
+
+ private final String display;
+
+ private CompileResult(String display) {
+ this.display = display;
+ }
+
+ public String toString() {
+ return display;
+ }
+ }
+
+ /**
+ * Compile the specified template.
+ * Any compile problems are included in the compilation unit returned.
+ * @param templatePath the template path of the JET file
+ * @return a {@link CompileResult} indicating the result of the compilation.
+ * @throws IllegalStateException if called after a call to {@link #finish()}
+ */
+ public abstract CompileResult compile(String templatePath);
+
+
+ /**
+ * Retrieve the current compiler state.
+ */
+ public abstract Serializable getMemento();
+
+ /**
+ * Finish compilation by generating cross template information such as template loaders or maps.
+ */
+ public abstract void finish();
+
+ /**
+ * Remove any compiler created artifacts.
+ * @throws IllegalStateException if {@link #compile(String)} has already been called
+ */
+ public abstract void clean();
+
+ /**
+ * Remove the previously generated output for a template, corresponding to the removal
+ * of a tempalte
+ * @param templatePath a tempate path
+ */
+ public abstract void removeTemplate(String templatePath);
+
+ /**
+ * Return the tag libaries referenced by the compilation
+ * @return a non-null array of tag library ids.
+ */
+ public abstract String[] getTagLibaryDependencies();
+
+ /**
+ * Test whether the template path has a recognized JET extension according to this compiler.
+ * @param templatePath a tempate path
+ * @return <code>true</code> if the tempalte has a recognized template extension
+ */
+ public abstract boolean isTemplate(String templatePath);
+
+ /**
+ * Return the list of files needing recompilation if the file represented by
+ * changedFilePath has changed.
+ * Note that if changeFilePath is a a template path, the the template path
+ * is not returned in the list.
+ * Note also that this method depends on the compiler maintaining incremental
+ * compilation state and is useful to an incremental builder.
+ * @param changedFilePath a template or included file path
+ * @return a possibly empty list of templates paths to recompile.
+ */
+ public abstract String[] getAffectedTemplatePaths(String changedFilePath);
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependencies.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependencies.java
new file mode 100644
index 0000000..5c49d9b
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependencies.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 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.jet.internal.core.compiler;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Include dependencies for a JET transformation/compiler
+ *
+ */
+public class IncludeDependencies implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3810681358080776836L;
+
+ private static final String[] EMPTY_STRINGS_ARRAY = new String[0];
+
+ private Map dependentPathToTemplates = new HashMap(); // Map<String, Set<String>>
+ private transient Map templatePathToDependencies = new HashMap(); // Map<String, Set<String>>
+
+ public void addDependencies(String templatePath, String dependencies[]) {
+ Set dependents = (Set)templatePathToDependencies.get(templatePath);
+ if(dependents == null) {
+ dependents = new HashSet(Arrays.asList(dependencies));
+ templatePathToDependencies.put(templatePath, dependents);
+ } else {
+ dependents.addAll(Arrays.asList(dependencies));
+ }
+
+ for (int i = 0; i < dependencies.length; i++) {
+ final String dependentPath = dependencies[i];
+
+ Set templates = (Set) dependentPathToTemplates.get(dependentPath);
+
+ if(templates == null) {
+ templates = new HashSet();
+ dependentPathToTemplates.put(dependentPath, templates);
+ }
+
+ templates.add(templatePath);
+ }
+ }
+
+ public void removeDependencies(String templatePath) {
+ Set dependencies = (Set) templatePathToDependencies.remove(templatePath);
+ if(dependencies != null) {
+ for (Iterator i = dependencies.iterator(); i.hasNext();) {
+ String dependentPath = (String) i.next();
+ final Set templates = (Set) dependentPathToTemplates.get(dependentPath);
+ if(templates != null) {
+ templates.remove(templatePath);
+ if(templates.size() == 0) {
+ dependentPathToTemplates.remove(dependentPath);
+ }
+ }
+ }
+ }
+ }
+
+ public String[] getAffectedTemplates(String dependency) {
+ final Set templates = (Set) dependentPathToTemplates.get(dependency);
+ return templates == null ? EMPTY_STRINGS_ARRAY : (String[])templates.toArray(EMPTY_STRINGS_ARRAY);
+ }
+
+ private void writeObject(ObjectOutputStream s) throws IOException {
+ s.defaultWriteObject();
+ }
+
+ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException{
+ s.defaultReadObject();
+
+ // rebuild the reverse map
+ templatePathToDependencies = new HashMap();
+ for (Iterator i = dependentPathToTemplates.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String dependentPath = (String) entry.getKey();
+ Set templates = (Set) entry.getValue();
+ for (Iterator j = templates.iterator(); j.hasNext();) {
+ String templatePath = (String) j.next();
+ Set dependents = (Set)templatePathToDependencies.get(templatePath);
+ if(dependents == null) {
+ dependents = new HashSet();
+ templatePathToDependencies.put(templatePath, dependents);
+ }
+
+ dependents.add(dependentPath);
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependenciesUtil.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependenciesUtil.java
new file mode 100644
index 0000000..e374870
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/IncludeDependenciesUtil.java
@@ -0,0 +1,55 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: IncludeDependenciesUtil.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.compiler;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jet.core.parser.ast.IncludedContent;
+import org.eclipse.jet.core.parser.ast.JETASTVisitor;
+import org.eclipse.jet.core.parser.ast.JETCompilationUnit;
+
+/**
+ * Utility for calculating include dependencies of a compilation unit
+ */
+public final class IncludeDependenciesUtil extends JETASTVisitor {
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ /**
+ * Return the include dependencies for a compilation unit.
+ * @param cu a compilation unit
+ * @return a possibly empty array of template paths
+ */
+ public static String[] getDependencies(JETCompilationUnit cu) {
+ final IncludeDependenciesUtil util = new IncludeDependenciesUtil();
+ cu.accept(util);
+
+ return (String[]) util.dependencies.toArray(EMPTY_STRING_ARRAY);
+ }
+
+ private final Set dependencies = new HashSet();
+
+ private IncludeDependenciesUtil() {
+ // prevent instantiation
+ }
+
+ public boolean visit(IncludedContent content) {
+ dependencies.add(content.getTemplatePath());
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/UniqueNameGenerator.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/UniqueNameGenerator.java
new file mode 100644
index 0000000..d4692b8
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/compiler/UniqueNameGenerator.java
@@ -0,0 +1,221 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: UniqueNameGenerator.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.compiler;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.ast.JETCompilationUnit;
+
+
+/**
+ * The class is responsible for managing the generation of Java class names for
+ * JET templates
+ */
+public class UniqueNameGenerator
+{
+
+ private final Map pathToFQJavaClassMap;
+
+ private final String defaultJavaPackage;
+
+ private final Map fqJavaClassToPathMap;
+
+ public UniqueNameGenerator(Map pathToFQJavaClassMap, Map fqJavaClassToPathMap, String defaultJavaPackage)
+ {
+ this.pathToFQJavaClassMap = new HashMap(pathToFQJavaClassMap);
+ this.fqJavaClassToPathMap = new HashMap(fqJavaClassToPathMap);
+ this.defaultJavaPackage = defaultJavaPackage;
+
+ }
+
+ /**
+ * Ensure the the compilation unit has Java output package and class
+ * set. If not, generate names for these, taking into account that
+ * a name may have been generated for them already.
+ * @param templatePath the template path of the compilation unit
+ * @param cu the compilation unit
+ */
+ public void ensureJavaOutputSet(String templatePath, JETCompilationUnit cu) throws DuplicateGeneratedClassException
+ {
+ String fqn;
+
+ // See if we already have a name for this...
+ final String existingQualifiedClass = (String)pathToFQJavaClassMap.get(templatePath);
+ if (existingQualifiedClass == null)
+ {
+ // we haven't generated a name before, do it now...
+
+ if (cu.getOutputJavaPackage() == null)
+ {
+ cu.setOutputJavaPackage(defaultJavaPackage);
+ }
+ if (cu.getOutputJavaClassName() == null)
+ {
+ // check for conflicts because we have name mangled
+ // or for identically named files in different directories.
+ String baseClassName = makeJavaClassName(templateBaseName(templatePath));
+
+ String className = baseClassName;
+ String potentialQualifiedName = makeFullyQualifiedJavaName(cu.getOutputJavaPackage(), className);
+
+ for (int i = 0; fqJavaClassToPathMap.get(potentialQualifiedName) != null; i++)
+ {
+ className = baseClassName + "_" + i; //$NON-NLS-1$
+ potentialQualifiedName = makeFullyQualifiedJavaName(cu.getOutputJavaPackage(), className);
+ }
+
+ cu.setOutputJavaClassName(className);
+ fqn = makeFullyQualifiedJavaName(cu.getOutputJavaPackage(), cu.getOutputJavaClassName());
+ }
+ else
+ {
+ // check for a name collision
+ fqn = makeFullyQualifiedJavaName(cu.getOutputJavaPackage(), cu.getOutputJavaClassName());
+ final String otherTemplatePath = (String)fqJavaClassToPathMap.get(fqn);
+ if (otherTemplatePath != null)
+ {
+ throw new DuplicateGeneratedClassException(templatePath, otherTemplatePath, fqn);
+ }
+ }
+ }
+ else
+ {
+ fqn = existingQualifiedClass;
+ int index = existingQualifiedClass.lastIndexOf('.');
+ String existingPackage = index == -1 ? "" : existingQualifiedClass.substring(0, index); //$NON-NLS-1$
+ String existingClass = index == -1 ? existingQualifiedClass : existingQualifiedClass.substring(index + 1);
+
+ if (cu.getOutputJavaPackage() == null)
+ {
+ cu.setOutputJavaPackage(existingPackage);
+ }
+ if (cu.getOutputJavaClassName() == null)
+ {
+ cu.setOutputJavaClassName(existingClass);
+ }
+ }
+
+ // update the maps
+ fqJavaClassToPathMap.put(fqn, templatePath);
+ pathToFQJavaClassMap.put(templatePath, fqn);
+ }
+
+ /**
+ * Return the template base name, that is the template name with extension and path prefix removed.
+ * @param templatePath a template path
+ * @return the base name
+ */
+ private String templateBaseName(String templatePath)
+ {
+ int lastSlash = templatePath.lastIndexOf('/');
+ String baseFile = lastSlash == -1 ? templatePath : templatePath.substring(lastSlash + 1);
+ int lastDot = baseFile.lastIndexOf('.');
+ return lastDot == -1 ? baseFile : baseFile.substring(0, lastDot);
+ }
+
+ /**
+ * Make a a Java class name from the base template name
+ * @param name the base template name (path and extension removed)
+ * @return
+ */
+ private String makeJavaClassName(String name)
+ {
+ StringBuffer result = new StringBuffer("_jet_"); //$NON-NLS-1$
+ for (int i = 0; i < name.length(); i++)
+ {
+ char c = name.charAt(i);
+ if (Character.isJavaIdentifierPart(c))
+ {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Make a fully qualified Java class name given the possibly null or empty Java package
+ * and a non-empty Java class name.
+ * @param javaPackage a Java package name or the empty string or <code>null</code>
+ * @param javaClassName a Java class name
+ * @return a fully qualified Java class name
+ */
+ private String makeFullyQualifiedJavaName(String javaPackage, String javaClassName)
+ {
+ return javaPackage == null || javaPackage.trim().length() == 0 ? javaClassName : javaPackage + '.' + javaClassName;
+ }
+
+ /**
+ * Return a copy for the map of template paths to fully qualified class names
+ * @return a map of fully qualified class names (String) keyed by template path (String)
+ */
+ public Map getPathToFQNMap()
+ {
+ return new HashMap(pathToFQJavaClassMap);
+ }
+
+ /**
+ * Return a copy of the map of fully qualified class names to the corresponding JET template path.
+ * @return a map of template paths (String) keyd by fully qualified class name
+ */
+ public Map getFQNToPathMap()
+ {
+ return new HashMap(fqJavaClassToPathMap);
+ }
+
+ /**
+ * Return the assets generated by the compiler for the given templatePath
+ * @param templatePath a template path
+ * @return a possibly empty array of file paths relative
+ */
+ public String getGeneratedOutputPath(String templatePath)
+ {
+ final String fqn = (String)pathToFQJavaClassMap.get(templatePath);
+ return fqn == null ? null : fqn.replace('.', '/') + ".java"; //$NON-NLS-1$
+ }
+
+ /**
+ * Clean the unique name manager.
+ * @return the list of generated Java Class names the manager has forgotten.
+ */
+ public List clean()
+ {
+ List removedFQJavaClasses = new ArrayList(fqJavaClassToPathMap.keySet());
+ fqJavaClassToPathMap.clear();
+ pathToFQJavaClassMap.clear();
+ return removedFQJavaClasses;
+ }
+
+ /**
+ *
+ * @param templatePath
+ * @return
+ */
+ public String remove(String templatePath)
+ {
+ String outputPath = getGeneratedOutputPath(templatePath);
+ if (outputPath != null)
+ {
+ String fqName = (String)pathToFQJavaClassMap.remove(templatePath);
+ fqJavaClassToPathMap.remove(fqName);
+ }
+ return outputPath;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateInput.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateInput.java
new file mode 100644
index 0000000..211d1f5
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateInput.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 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.jet.internal.core.parser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URL;
+
+import org.eclipse.jet.core.parser.ITemplateInput;
+import org.eclipse.jet.core.parser.TemplateInputException;
+
+/**
+ * Standard inplementation of ITemplateInput
+ *
+ */
+public class DefaultTemplateInput implements ITemplateInput {
+
+ private final URI baseLocation;
+ private final String templatePath;
+ private final String encoding;
+
+
+ public DefaultTemplateInput(URI baseLocation, String templatePath, String encoding) {
+ this.baseLocation = baseLocation;
+ this.templatePath = templatePath;
+ this.encoding = encoding;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.compiler.ITemplateInput#getBaseURL()
+ */
+ public URI getBaseLocation() {
+ return baseLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.tools.compiler.ITemplateInput#getTemplatePath()
+ */
+ public String getTemplatePath() {
+ return templatePath;
+ }
+
+ public Reader getReader() throws TemplateInputException {
+ // TODO Auto-generated method stub
+ try {
+ URL url = baseLocation.resolve(templatePath).toURL();
+ return new BufferedReader(new InputStreamReader(url.openStream(), getEncoding()));
+ } catch (IOException e) {
+ throw new TemplateInputException(e);
+ }
+ }
+
+
+ public String getEncoding() throws TemplateInputException {
+ return encoding;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateResolverHelper.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateResolverHelper.java
new file mode 100644
index 0000000..968e7fe
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/DefaultTemplateResolverHelper.java
@@ -0,0 +1,92 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: DefaultTemplateResolverHelper.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+
+import org.eclipse.jet.core.parser.ITemplateInput;
+import org.eclipse.jet.core.parser.ITemplateResolverHelper;
+
+/**
+ * @author pelder
+ */
+public class DefaultTemplateResolverHelper implements ITemplateResolverHelper
+{
+
+ private final URI baseLocation;
+
+ public DefaultTemplateResolverHelper(URI baseLocation)
+ {
+ this.baseLocation = baseLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.internal.parser.resources.ITemplateResolverHelper#createTemplateInput(java.net.URI, java.lang.String)
+ */
+ public ITemplateInput createTemplateInput(String templatePath)
+ {
+ String encoding = "UTF-8"; //$NON-NLS-1$
+ return new DefaultTemplateInput(baseLocation, templatePath, encoding);
+ }
+
+ /**
+ * Close an input stream, supressing IOExceptions for which we can do nothing.
+ * @param input an input stream
+ */
+ private void closeStream(InputStream input) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+
+ private InputStream openStream(URI baseLocation, String templatePath) {
+ try {
+ URL templateURL = baseLocation.resolve(templatePath).toURL();
+ final InputStream input = templateURL.openStream();
+ return input;
+ } catch (MalformedURLException e) {
+ // ignore;
+ } catch (IOException e) {
+ // ignore;
+ }
+ return null;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.internal.parser.resources.ITemplateResolverHelper#inputExists(java.net.URI, java.lang.String)
+ */
+ public boolean inputExists(String templatePath)
+ {
+ boolean exists = false;
+ final InputStream input = openStream(baseLocation, templatePath);
+
+ if(input != null) {
+ exists = true;
+ closeStream(input);
+ }
+
+ return exists;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/ElementStack.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/ElementStack.java
new file mode 100644
index 0000000..8601e66
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/ElementStack.java
@@ -0,0 +1,137 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: ElementStack.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jet.core.parser.ast.XMLBodyElement;
+
+
+/**
+ * Stack of JET AST Elements used by parser.
+ */
+public final class ElementStack
+{
+
+ private final List stack = new ArrayList();
+
+ /**
+ *
+ */
+ public ElementStack()
+ {
+ super();
+ }
+
+ /**
+ * Push an XML element on the active element stack
+ * @param element
+ */
+ public void push(XMLBodyElement element)
+ {
+ stack.add(element);
+ }
+
+ /**
+ * Remove an XML element from the active element stack
+ * @return the popped element
+ * @throws IllegalStateException if the stack is empty
+ */
+ public XMLBodyElement pop()
+ {
+ if (stack.size() == 0)
+ {
+ throw new IllegalStateException("stack is empty"); //$NON-NLS-1$
+ }
+ return (XMLBodyElement)stack.remove(stack.size() - 1);
+ }
+
+ /**
+ * Return the top element on the active element statkc
+ * @return the top element
+ * @throws IllegalStateException if the stack is empty
+ */
+ public XMLBodyElement peek()
+ {
+ if (stack.size() == 0)
+ {
+ throw new IllegalStateException("stack is empty"); //$NON-NLS-1$
+ }
+ return (XMLBodyElement)stack.get(stack.size() - 1);
+
+ }
+
+ /**
+ * Test if the stack is empty
+ * @return <code>true</code> if empty, <code>false</code> otherwise
+ */
+ public boolean isEmpty()
+ {
+ return stack.size() == 0;
+ }
+
+ /**
+ * Find the index of the element with the specified name that is closest to the top of the stack.
+ * @param name the element name to search for
+ * @return the found element's index or -1 if not found
+ */
+ public int findElementIndex(String name)
+ {
+ for (int i = stack.size() - 1; i >= 0; i--)
+ {
+ XMLBodyElement element = get(i);
+ if (element.getName().equalsIgnoreCase(name))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Test if the passed index is that of the top-most element in the active element stack.
+ * @param index the test index
+ * @return <code>true</code> if the index represents the top-most element, <code>false</code> otherwise.
+ * @throws IllegalArgumentException if the index is less than 0 or larger than the stack size.
+ */
+ public boolean isAtTop(int index)
+ {
+ if (index < 0 || index >= stack.size())
+ {
+ throw new IllegalArgumentException();
+ }
+ return index == stack.size() - 1;
+ }
+
+ /**
+ * Return the element at the specified index
+ * @param index
+ * @return the element
+ * @throws IllegalArgumentException if the index is less than 0 or larger than the stack size.
+ *
+ */
+ public XMLBodyElement get(int index)
+ {
+ if (index < 0 || index >= stack.size())
+ {
+ throw new IllegalArgumentException();
+ }
+ return (XMLBodyElement)stack.get(index);
+ }
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/IncludeAlternativesTracker.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/IncludeAlternativesTracker.java
new file mode 100644
index 0000000..afa448b
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/IncludeAlternativesTracker.java
@@ -0,0 +1,181 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: IncludeAlternativesTracker.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser;
+
+import java.util.Stack;
+
+/**
+ * Track processing and nesting of &at;include's with &at;start and &at;end directives for the JET compiler.
+ */
+public class IncludeAlternativesTracker {
+ private final Stack stack = new Stack();
+
+ private static final class State {
+
+ public static final State INITIAL = new State("INITIAL"); //$NON-NLS-1$
+
+ public static final State START = new State("START"); //$NON-NLS-1$
+
+ public static final State END = new State("END"); //$NON-NLS-1$
+
+ private final String display;
+
+ private State(String display) {
+ this.display = display;
+ }
+
+ public String toString() {
+ return display;
+ }
+ }
+
+ private static final class IncludeWithAlternative {
+
+ private State state;
+
+ private final boolean processAlternative;
+
+ private final boolean parentCompileEnabled;
+
+ private final Object includeDirective;
+
+ private Object startDirective;
+
+ public IncludeWithAlternative(Object includeDirective,
+ boolean processAlternative, boolean parentCompileEnabled) {
+ this.includeDirective = includeDirective;
+ this.processAlternative = processAlternative;
+ this.parentCompileEnabled = parentCompileEnabled;
+ this.state = State.INITIAL;
+
+ }
+
+ public void doStart(Object startDirective) throws IllegalStateException {
+ this.startDirective = startDirective;
+ if (state != State.INITIAL) {
+ throw new IllegalStateException();
+ }
+
+ state = State.START;
+ }
+
+ public void doEnd(Object startDirective) throws IllegalStateException {
+ if (state != State.START) {
+ throw new IllegalStateException();
+ }
+
+ state = State.END;
+ }
+
+ public boolean isProcessAlternative() {
+ return processAlternative;
+ }
+
+ public boolean isCompileEnabled() throws IllegalStateException {
+ if (state == State.END) {
+ throw new IllegalStateException();
+ }
+ return parentCompileEnabled
+ && (state != State.START || isProcessAlternative());
+ }
+
+ public boolean isStateInitial() {
+ return state == State.INITIAL;
+ }
+
+ public boolean isStateStart() {
+ return state == State.START;
+ }
+
+ public Object getIncludeDirective() {
+ return includeDirective;
+ }
+
+ public Object getStartDirective() {
+ return startDirective;
+ }
+ }
+
+ /**
+ * Test whether compiling is currently enabled
+ * @return <code>true</code> if compiling currently enabled.
+ */
+ public boolean isCompileEnabled() {
+ return stack.isEmpty()
+ || ((IncludeWithAlternative) stack.peek()).isCompileEnabled();
+ }
+
+ /**
+ * Start tracking an <code>&lt%&at;include fail="alternative" ... %&gt;</code> directive
+ * @param directive the include directive
+ * @param processAlternative true if the content between &at;start and &at;end directives should be processed by the compiler.
+ */
+ public void addIncludeWithAlternative(Object directive,
+ boolean processAlternative) {
+ stack.push(new IncludeWithAlternative(directive, processAlternative,
+ isCompileEnabled()));
+ }
+
+ /**
+ * Mark the beginning of an include alternative, a <code>&lt;%&at;start%&gt;</code> directive.
+ * @param directive the Start directive
+ * @throws IllegalStateException if there is no preceeding <code>at;include fail="alternative"</code> directive.
+ */
+ public void startAlternative(Object directive) throws IllegalStateException {
+ if (stack.isEmpty()) {
+ throw new IllegalStateException();
+ }
+
+ IncludeWithAlternative include = (IncludeWithAlternative) stack.peek();
+ include.doStart(directive);
+ }
+
+ /**
+ * Mark the end of an include alternative, a <code>&lt;%&at;end%&gt;</code> directive.
+ * @param directive
+ * @throws IllegalStateException
+ */
+ public void endAlternative(Object directive) throws IllegalStateException {
+ if (stack.isEmpty()) {
+ throw new IllegalStateException();
+ }
+ // only peek on the stack, until we're certain doEnd succeeds
+ IncludeWithAlternative include = (IncludeWithAlternative) stack.peek();
+ include.doEnd(directive);
+ stack.pop();
+ }
+
+ public static interface ValidationProblemReporter {
+
+ void reportMissingStart(Object includeDirective);
+
+ void reportMissingEnd(Object startDirective);
+
+ }
+
+ public void validateStackIsEmpty(ValidationProblemReporter reporter) {
+ while (!stack.isEmpty()) {
+ IncludeWithAlternative include = (IncludeWithAlternative) stack
+ .pop();
+ if (include.isStateInitial()) {
+ reporter.reportMissingStart(include.getIncludeDirective());
+ } else if (include.isStateStart()) {
+ reporter.reportMissingEnd(include.getStartDirective());
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET1Parser.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET1Parser.java
new file mode 100644
index 0000000..653c336
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET1Parser.java
@@ -0,0 +1,598 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: InternalJET1Parser.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.internal.core.parser;
+
+import java.io.CharArrayReader;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.eclipse.jet.core.parser.IJETParser;
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ITemplateInput;
+import org.eclipse.jet.core.parser.ITemplateResolver;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+import org.eclipse.jet.core.parser.RecursiveIncludeException;
+import org.eclipse.jet.core.parser.TemplateInputException;
+import org.eclipse.jet.core.parser.ast.BodyElement;
+import org.eclipse.jet.core.parser.ast.Comment;
+import org.eclipse.jet.core.parser.ast.IncludedContent;
+import org.eclipse.jet.core.parser.ast.JETAST;
+import org.eclipse.jet.core.parser.ast.JETCompilationUnit;
+import org.eclipse.jet.core.parser.ast.JETDirective;
+import org.eclipse.jet.core.parser.ast.JavaDeclaration;
+import org.eclipse.jet.core.parser.ast.JavaExpression;
+import org.eclipse.jet.core.parser.ast.JavaScriptlet;
+import org.eclipse.jet.core.parser.ast.TextElement;
+import org.eclipse.jet.internal.core.parser.jasper.CommentElementDelegate;
+import org.eclipse.jet.internal.core.parser.jasper.DeclarationElementDelegate;
+import org.eclipse.jet.internal.core.parser.jasper.ErrorRedirectingCoreElementDelegate;
+import org.eclipse.jet.internal.core.parser.jasper.JETCoreElement;
+import org.eclipse.jet.internal.core.parser.jasper.JETException;
+import org.eclipse.jet.internal.core.parser.jasper.JETMark;
+import org.eclipse.jet.internal.core.parser.jasper.JETParseEventListener2;
+import org.eclipse.jet.internal.core.parser.jasper.JETParser;
+import org.eclipse.jet.internal.core.parser.jasper.JETReader;
+import org.eclipse.jet.internal.l10n.JET2Messages;
+
+/**
+ * JET Parser Listener used by the JET1 Syntax
+ *
+ */
+public class InternalJET1Parser implements JETParseEventListener2, IJETParser {
+
+ /**
+ * Enumeratin of IncludeFailActions
+ *
+ */
+ public final static class IncludeFailAction {
+
+ public static final IncludeFailAction ERROR = new IncludeFailAction(
+ "error"); //$NON-NLS-1$
+
+ public static final IncludeFailAction SILENT = new IncludeFailAction(
+ "silent"); //$NON-NLS-1$
+
+ public static final IncludeFailAction ALTERNATIVE = new IncludeFailAction(
+ "alternative"); //$NON-NLS-1$
+
+ private final String displayValue;
+
+ private IncludeFailAction(String displayValue) {
+ this.displayValue = displayValue;
+ }
+
+ public String toString() {
+ return displayValue;
+ }
+
+ public static IncludeFailAction getAction(String action) {
+ if ("alternative".equalsIgnoreCase(action)) { //$NON-NLS-1$
+ return ALTERNATIVE;
+ } else if ("silent".equalsIgnoreCase(action)) { //$NON-NLS-1$
+ return SILENT;
+ } else {
+ return ERROR;
+ }
+ }
+ }
+
+ private static final String JET__DIRECTIVE = "jet"; //$NON-NLS-1$
+
+ private static final String INCLUDE__DIRECTIVE = "include"; //$NON-NLS-1$
+
+ private static final String FILE__ATTR = "file"; //$NON-NLS-1$
+
+ private static final String FAIL__ATTR = "fail"; //$NON-NLS-1$
+
+ private static final String START__DIRECTIVE = "start"; //$NON-NLS-1$
+
+ private static final String END__DIRECTIVE = "end"; //$NON-NLS-1$
+
+ private final IncludeAlternativesTracker includeAlternativesTracker = new IncludeAlternativesTracker();
+
+ /**
+ * Stack of open includedContent elements
+ */
+ private final Stack includedContentStack = new Stack();
+
+ /**
+ * Stack of ITemplateInput objects.
+ */
+ private final Stack templateInputs = new Stack();
+
+ private JETCompilationUnit compilationUnit;
+
+ private JETAST ast;
+
+ private JETReader reader;
+
+ private final ITemplateResolver templateResolver;
+
+ public InternalJET1Parser(ITemplateResolver templateResolver) {
+ this.templateResolver = templateResolver;
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#beginPageProcessing()
+ */
+ public void beginPageProcessing() {
+ // nothing to do
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleDirective(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleDirective(String directive, JETMark start, JETMark stop,
+ Map attributes) {
+ JETDirective directiveElement = ast.newJETDirective(start.getLine(), start
+ .getCol(), start.getCursor(), stop.getCursor() + 1, directive,
+ attributes);
+
+ boolean compileEnabled = includeAlternativesTracker.isCompileEnabled();
+ // although a directive may appear nested, it really isn't. Add it to the compilation unit.
+
+ if (JET__DIRECTIVE.equalsIgnoreCase(directive)) {
+ if(compileEnabled) {
+ addBodyElement(directiveElement);
+ handleJetDirective(start, stop, attributes);
+ }
+ } else if (INCLUDE__DIRECTIVE.equalsIgnoreCase(directive)) {
+ // add the include to the AST only if the section is enabled...
+ if(compileEnabled) {
+ addBodyElement(directiveElement);
+ }
+ handleIncludeDirective(directiveElement, start, stop, attributes);
+ } else if (START__DIRECTIVE.equalsIgnoreCase(directive)) {
+ try {
+ includeAlternativesTracker.startAlternative(directiveElement);
+ // add the start to the AST only if, AFTER processing the @start,
+ // the section is enabled...
+ if(includeAlternativesTracker.isCompileEnabled()) {
+ addBodyElement(directiveElement);
+ }
+ } catch (IllegalStateException e) {
+ recordProblem(ProblemSeverity.ERROR,
+ IProblem.StartDirectiveOutOfContext,
+ JET2Messages.JET2Compiler_StartDirectiveOutOfContext,
+ new Object[] { directive }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ } else if (END__DIRECTIVE.equalsIgnoreCase(directive)) {
+ try {
+ // add the end to the AST only if the secion is enabled...
+ if(compileEnabled) {
+ addBodyElement(directiveElement);
+ }
+ includeAlternativesTracker.endAlternative(directiveElement);
+ } catch (IllegalStateException e) {
+ recordProblem(ProblemSeverity.ERROR,
+ IProblem.EndDirectiveOutOfContext,
+ JET2Messages.JET2Compiler_EndDirectiveOutOfContext,
+ new Object[] { directive }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ } else if (compileEnabled) {
+ recordProblem(ProblemSeverity.WARNING,
+ IProblem.UnsupportedDirective,
+ JET2Messages.ASTCompilerParseListener_UnsupportedDirective,
+ new Object[] { directive }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ }
+
+ private static Set knownIncludeAttributes = new LinkedHashSet(Arrays
+ .asList(new String[] { FILE__ATTR, FAIL__ATTR, }));
+
+ private void handleIncludeDirective(JETDirective directive, JETMark start,
+ JETMark stop, Map attributes) {
+ validateAttributes(start, stop, attributes, knownIncludeAttributes,
+ Collections.EMPTY_SET);
+
+ String file = (String) attributes.get(FILE__ATTR);
+ if (file == null) {
+ missingRequiredAttribute(start, stop, INCLUDE__DIRECTIVE,
+ FILE__ATTR);
+ return;
+ }
+
+ final IncludeFailAction failAction = IncludeFailAction
+ .getAction((String) attributes.get(FAIL__ATTR));
+
+ final boolean includePushed = includeAlternativesTracker.isCompileEnabled()
+ && doPushInclude(file);
+
+ if(includePushed) {
+ ITemplateInput templateInput = (ITemplateInput) templateInputs.peek();
+ final IncludedContent includedContent = ast.newIncludedContent(start.getLine(), start.getCol(), start.getCursor(), stop.getCursor(),
+ templateInput.getBaseLocation(), templateInput.getTemplatePath());
+ addBodyElement(includedContent);
+ includedContentStack.push(includedContent);
+ } else if (includeAlternativesTracker.isCompileEnabled()) {
+ if (failAction == IncludeFailAction.ERROR) {
+ recordProblem(ProblemSeverity.ERROR, IProblem.MissingFile,
+ JET2Messages.JET2Compiler_MissingFile,
+ new Object[] { file }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ }
+ if (failAction == IncludeFailAction.ALTERNATIVE) {
+ boolean processAlternative = includeAlternativesTracker
+ .isCompileEnabled()
+ && !includePushed;
+ includeAlternativesTracker.addIncludeWithAlternative(directive,
+ processAlternative);
+ }
+ }
+
+ private boolean doPushInclude(String relativePath) {
+ ITemplateInput[] activeInputs = (ITemplateInput[]) templateInputs.toArray(new ITemplateInput[templateInputs.size()]);
+
+ if(activeInputs.length > 0) {
+ try {
+ final ITemplateInput includedInput = templateResolver.getIncludedInput(relativePath, activeInputs);
+
+ if(includedInput != null) {
+ reader.stackStream(includedInput.getBaseLocation().toString(), includedInput.getTemplatePath(), includedInput.getReader());
+
+ templateInputs.push(includedInput);
+
+ return true;
+ }
+ } catch (JETException e) {
+ // fall through
+ } catch (TemplateInputException e) {
+ // fall through
+ } catch (RecursiveIncludeException e) {
+ // fall through
+ }
+ }
+
+ return false;
+ }
+
+ private void validateAttributes(JETMark start, JETMark stop,
+ Map attributes, Set knownAttributes, Set deprecatedAttributes) {
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
+ String attrName = (String) i.next();
+ if (!knownAttributes.contains(attrName)) {
+ recordProblem(ProblemSeverity.ERROR,
+ IProblem.UnknownAttributeInTag,
+ JET2Messages.JET2Compiler_UnknownAttribute,
+ new Object[] { attrName }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ if (deprecatedAttributes.contains(attrName)) {
+ recordProblem(ProblemSeverity.WARNING,
+ IProblem.DeprecatedAttribute,
+ JET2Messages.JET2Compiler_DeprecatedAttribute,
+ new Object[] { attrName }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ }
+ }
+
+ private void missingRequiredAttribute(JETMark start, JETMark stop,
+ String directive, String attribute) {
+ compilationUnit.createProblem(ProblemSeverity.ERROR,
+ IProblem.MissingRequiredAttribute,
+ JET2Messages.JET2Compiler_MissingDirectiveAttribute,
+ new Object[] { directive, attribute },
+ // start.getLocalFile(),
+ start.getCursor(), stop.getCursor(), start.getLine(), start
+ .getCol());
+ }
+
+ private static Set knownJETAttributes = new LinkedHashSet(Arrays
+ .asList(new String[] { "skeleton", //$NON-NLS-1$
+ "package", //$NON-NLS-1$
+ "imports", //$NON-NLS-1$
+ "class", //$NON-NLS-1$
+ "nlString", //$NON-NLS-1$
+ "startTag", //$NON-NLS-1$
+ "endTag", //$NON-NLS-1$
+ "version", //$NON-NLS-1$
+ }));
+
+ private static Set deprecatedJETAttributes = new LinkedHashSet(Arrays
+ .asList(new String[] { "skeleton", //$NON-NLS-1$
+ "nlString", //$NON-NLS-1$
+ }));
+
+ private JETParser parser;
+
+ private void handleJetDirective(JETMark start, JETMark stop, Map attributes) {
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
+ String attrName = (String) i.next();
+ if (!knownJETAttributes.contains(attrName)) {
+ recordProblem(ProblemSeverity.ERROR,
+ IProblem.UnknownAttributeInTag,
+ JET2Messages.JET2Compiler_UnknownAttribute,
+ new Object[] { attrName }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ if (deprecatedJETAttributes.contains(attrName)) {
+ recordProblem(ProblemSeverity.WARNING,
+ IProblem.DeprecatedAttribute,
+ JET2Messages.JET2Compiler_DeprecatedAttribute,
+ new Object[] { attrName }, start.getCursor(), stop
+ .getCursor(), start.getLine(), start.getCol());
+ }
+ }
+
+ String pkg = (String) attributes.get("package"); //$NON-NLS-1$
+ String cls = (String) attributes.get("class"); //$NON-NLS-1$
+ String importStr = (String) attributes.get("imports"); //$NON-NLS-1$
+ String startTag = (String) attributes.get("startTag"); //$NON-NLS-1$
+ String endTag = (String) attributes.get("endTag"); //$NON-NLS-1$
+
+ if (pkg != null) {
+ compilationUnit.setOutputJavaPackage(pkg);
+ }
+ if (cls != null) {
+ compilationUnit.setOutputJavaClassName(cls);
+ }
+ if (importStr != null) {
+ String[] imports = importStr.split("\\s+"); //$NON-NLS-1$
+ compilationUnit.addImports(Arrays.asList(imports));
+ }
+
+ if (startTag != null) {
+ parser.setStartTag(startTag);
+ }
+ if (endTag != null) {
+ parser.setEndTag(endTag);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleExpression(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleExpression(JETMark start, JETMark stop, Map attributes) {
+ if(!includeAlternativesTracker.isCompileEnabled()) {
+ return;
+ }
+ JavaExpression expression = ast.newJavaExpression(start.getLine(),
+ start.getCol(), start.getCursor() - 3, stop.getCursor() + 2,
+ start.getCursor(), stop.getCursor(), reader.getChars(start,
+ stop));
+
+ addBodyElement(expression);
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleCharData(char[])
+ */
+ public void handleCharData(char[] chars) {
+ if(!includeAlternativesTracker.isCompileEnabled()) {
+ return;
+ }
+ TextElement text = ast.newTextElement(chars);
+
+ addBodyElement(text);
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#endPageProcessing()
+ */
+ public void endPageProcessing() {
+ if(templateInputs.size() > 0) {
+ templateInputs.pop();
+ }
+ if(includedContentStack.size() > 0) {
+ includedContentStack.pop();
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleScriptlet(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleScriptlet(JETMark start, JETMark stop, Map attributes) {
+ if(!includeAlternativesTracker.isCompileEnabled()) {
+ return;
+ }
+ JavaScriptlet scriplet = ast.newJavaScriptlet(start.getLine(), start
+ .getCol(), start.getCursor() - 3, stop.getCursor() + 2, start
+ .getCursor(), stop.getCursor(), reader.getChars(start, stop));
+
+ addBodyElement(scriplet);
+
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleComment(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark)
+ */
+ public void handleComment(JETMark start, JETMark stop) {
+ if(!includeAlternativesTracker.isCompileEnabled()) {
+ return;
+ }
+ Comment comment = ast.newComment(start.getLine(), start.getCol(), start
+ .getCursor() - 4, stop.getCursor() + 4, start.getCursor(), stop
+ .getCursor(), reader.getChars(start, stop));
+
+ addBodyElement(comment);
+ }
+
+ /**
+ * @param bodyElement
+ */
+ private void addBodyElement(BodyElement bodyElement) {
+ if (includedContentStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(bodyElement);
+ }
+ else
+ {
+ IncludedContent topElement = (IncludedContent)includedContentStack.peek();
+
+ topElement.addBodyElement(bodyElement);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleDeclaration(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark)
+ */
+ public void handleDeclaration(JETMark start, JETMark stop) {
+ if(!includeAlternativesTracker.isCompileEnabled()) {
+ return;
+ }
+ JavaDeclaration decl = ast.newJavaDeclaration(start.getLine(), start
+ .getCol(), start.getCursor() - 3, stop.getCursor() + 2, start
+ .getCursor(), stop.getCursor(), reader.getChars(start, stop));
+
+ addBodyElement(decl);
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleXMLEndTag(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark)
+ */
+ public void handleXMLEndTag(String tagName, JETMark start, JETMark stop) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleXMLEmptyTag(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleXMLEmptyTag(String tagName, JETMark start, JETMark stop,
+ Map attributeMap) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleXMLStartTag(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleXMLStartTag(String tagName, JETMark start, JETMark stop,
+ Map attributeMap) {
+
+ throw new IllegalStateException();
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#isKnownTag(java.lang.String)
+ */
+ public boolean isKnownTag(String tagName) {
+ return false;
+ }
+
+ /**
+ * Return the compilation unit created as a result of handling the JET2 parser events.
+ * @return compilation unit
+ */
+ public JETCompilationUnit getCompilationUnit() {
+ return compilationUnit;
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#recordProblem(org.eclipse.jet.compiler.Problem.ProblemSeverity, int, java.lang.String, java.lang.Object[], int, int, int, int)
+ */
+ public void recordProblem(ProblemSeverity severity, int problemId,
+ String message, Object[] msgArgs, int start, int end, int line,
+ int colOffset) {
+ compilationUnit.createProblem(severity, problemId, message, msgArgs,
+ start, end, line, colOffset);
+ }
+
+ /**
+ * @param reader
+ * @return
+ */
+ private JETParser configureParser(JETReader reader) {
+ JETParser.Directive directive = new JETParser.Directive();
+ directive.getDirectives().add("jet"); //$NON-NLS-1$
+ directive.getDirectives().add("include"); //$NON-NLS-1$
+ directive.getDirectives().add("start"); //$NON-NLS-1$
+ directive.getDirectives().add("end"); //$NON-NLS-1$
+ JETCoreElement[] coreElements = new JETCoreElement[] {
+ new ErrorRedirectingCoreElementDelegate(directive),
+ new ErrorRedirectingCoreElementDelegate(
+ new JETParser.Expression()),
+ new ErrorRedirectingCoreElementDelegate(
+ new CommentElementDelegate()),
+ new ErrorRedirectingCoreElementDelegate(
+ new DeclarationElementDelegate()),
+ new ErrorRedirectingCoreElementDelegate(
+ new JETParser.Scriptlet()), };
+
+ return new JETParser(reader, this, coreElements);
+ }
+
+ public boolean isKnownInvalidTagName(String tagName) {
+ return false;
+ }
+
+ public Object parse(String templatePath) {
+ final ITemplateInput templateInput = templateResolver
+ .getInput(templatePath);
+ templateInputs.push(templateInput);
+ final URI baseLocation = templateInput.getBaseLocation();
+ try {
+ if (compilationUnit == null) {
+ compilationUnit = new JETAST()
+ .newJETCompilationUnit(baseLocation, templatePath,
+ templateInput.getEncoding());
+ ast = compilationUnit.getAst();
+ }
+ reader = new JETReader(baseLocation == null ? null : baseLocation
+ .toString(), templatePath, templateInput.getReader());
+ intermalParse();
+ } catch (org.eclipse.jet.internal.core.parser.jasper.JETException e) {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e
+ .getLocalizedMessage(), null, 0, 0, 1, 1);
+ } catch (TemplateInputException e) {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e
+ .getLocalizedMessage(), null, 0, 0, 1, 1);
+ }
+ return compilationUnit;
+ }
+
+ public Object parse(char[] template) {
+ if (compilationUnit == null) {
+ compilationUnit = new JETAST()
+ .newJETCompilationUnit(null, "", null); //$NON-NLS-1$
+ ast = compilationUnit.getAst();
+ }
+ try {
+ reader = new JETReader(null, "", new CharArrayReader(template)); //$NON-NLS-1$
+ intermalParse();
+ } catch (JETException e) {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e
+ .getLocalizedMessage(), null, 0, 0, 1, 1);
+ }
+ return compilationUnit;
+ }
+
+ /**
+ * @throws JETException
+ */
+ private void intermalParse() throws JETException {
+ parser = configureParser(reader);
+ this.beginPageProcessing();
+ parser.parse();
+ this.endPageProcessing();
+ compilationUnit.accept(new TextTrimmingVisitor());
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET2Parser.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET2Parser.java
new file mode 100644
index 0000000..5a49408
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/InternalJET2Parser.java
@@ -0,0 +1,649 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: InternalJET2Parser.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.internal.core.parser;
+
+
+import java.io.CharArrayReader;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jet.core.parser.IJETParser;
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ITagLibraryResolver;
+import org.eclipse.jet.core.parser.ITemplateInput;
+import org.eclipse.jet.core.parser.ITemplateResolver;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+import org.eclipse.jet.core.parser.TemplateInputException;
+import org.eclipse.jet.core.parser.ast.Comment;
+import org.eclipse.jet.core.parser.ast.JETAST;
+import org.eclipse.jet.core.parser.ast.JETCompilationUnit;
+import org.eclipse.jet.core.parser.ast.JETDirective;
+import org.eclipse.jet.core.parser.ast.JavaDeclaration;
+import org.eclipse.jet.core.parser.ast.JavaExpression;
+import org.eclipse.jet.core.parser.ast.JavaScriptlet;
+import org.eclipse.jet.core.parser.ast.TagLibraryUsageManager;
+import org.eclipse.jet.core.parser.ast.TextElement;
+import org.eclipse.jet.core.parser.ast.XMLBodyElement;
+import org.eclipse.jet.core.parser.ast.XMLBodyElementEnd;
+import org.eclipse.jet.core.parser.ast.XMLEmptyElement;
+import org.eclipse.jet.internal.core.parser.jasper.CommentElementDelegate;
+import org.eclipse.jet.internal.core.parser.jasper.DeclarationElementDelegate;
+import org.eclipse.jet.internal.core.parser.jasper.ErrorRedirectingCoreElementDelegate;
+import org.eclipse.jet.internal.core.parser.jasper.JETCoreElement;
+import org.eclipse.jet.internal.core.parser.jasper.JETException;
+import org.eclipse.jet.internal.core.parser.jasper.JETMark;
+import org.eclipse.jet.internal.core.parser.jasper.JETParseEventListener2;
+import org.eclipse.jet.internal.core.parser.jasper.JETParser;
+import org.eclipse.jet.internal.core.parser.jasper.JETReader;
+import org.eclipse.jet.internal.core.parser.jasper.XMLElementDelegate;
+import org.eclipse.jet.internal.l10n.JET2Messages;
+import org.eclipse.jet.taglib.TagDefinition;
+import org.eclipse.jet.taglib.TagLibraryReference;
+
+
+/**
+ * JET Parser Listener used by the JET2 Syntax
+ *
+ */
+public class InternalJET2Parser implements JETParseEventListener2, IJETParser
+{
+
+ private static final String ID__ATTR = "id"; //$NON-NLS-1$
+
+ private static final String PREFIX__ATTR = "prefix"; //$NON-NLS-1$
+
+ private static final String TAGLIB__DIRECTIVE = "taglib"; //$NON-NLS-1$
+ private static final String JET__DIRECTIVE = "jet"; //$NON-NLS-1$
+ /**
+ * Stack of elements waiting for end tags.
+ */
+ private final ElementStack elementStack = new ElementStack();
+
+ private JETCompilationUnit compilationUnit;
+
+ private JETAST ast;
+
+ private final TagLibraryUsageManager tagLibManager;
+
+ private JETReader reader;
+
+ private final ITemplateResolver templateResolver;
+
+ public InternalJET2Parser(ITemplateResolver templateResolver, ITagLibraryResolver tagLibraryResolver, Map predefinedLibraryMap)
+ {
+ this.templateResolver = templateResolver;
+ tagLibManager = new TagLibraryUsageManager(predefinedLibraryMap, tagLibraryResolver);
+ }
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#beginPageProcessing()
+ */
+ public void beginPageProcessing()
+ {
+ // nothing to do
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleDirective(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleDirective(String directive, JETMark start, JETMark stop, Map attributes)
+ {
+ JETDirective directiveImpl = ast.newJETDirective(start.getLine(), start.getCol(), start.getCursor(), stop.getCursor() + 1, directive, attributes);
+
+ // although a directive may appear nested, it really isn't. Add it to the compilation unit.
+ // TODO Does this work for @start and @end? Probably NOT.
+ compilationUnit.addBodyElement(directiveImpl);
+
+ if (TAGLIB__DIRECTIVE.equalsIgnoreCase(directive))
+ {
+ handleTagLibDirective(start, stop, attributes);
+ }
+ else if(JET__DIRECTIVE.equalsIgnoreCase(directive))
+ {
+ handleJetDirective(start, stop, attributes);
+ } else {
+ recordProblem(ProblemSeverity.WARNING, IProblem.UnsupportedDirective,
+ JET2Messages.ASTCompilerParseListener_UnsupportedDirective,
+ new Object[] {directive}, start.getCursor(), stop.getCursor(), start.getLine(), start.getCol() );
+ }
+ }
+
+
+ private static Set knownJETAttributes =
+ new LinkedHashSet(Arrays.asList(new String[] {
+ "skeleton", //$NON-NLS-1$
+ "package", //$NON-NLS-1$
+ "imports", //$NON-NLS-1$
+ "class", //$NON-NLS-1$
+ "nlString", //$NON-NLS-1$
+ "startTag", //$NON-NLS-1$
+ "endTag", //$NON-NLS-1$
+ "version", //$NON-NLS-1$
+ }));
+ private static Set deprecatedJETAttributes =
+ new LinkedHashSet(Arrays.asList(new String[] {
+ "skeleton", //$NON-NLS-1$
+ "nlString", //$NON-NLS-1$
+ }));
+
+ private JETParser parser;
+ private void handleJetDirective(JETMark start, JETMark stop, Map attributes)
+ {
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext();)
+ {
+ String attrName = (String)i.next();
+ if(!knownJETAttributes.contains(attrName))
+ {
+ recordProblem(ProblemSeverity.ERROR, IProblem.UnknownAttributeInTag,
+ JET2Messages.JET2Compiler_UnknownAttribute, new Object[] {attrName},
+ start.getCursor(), stop.getCursor(), start.getLine(), start.getCol());
+ }
+ if(deprecatedJETAttributes.contains(attrName))
+ {
+ recordProblem(ProblemSeverity.WARNING, IProblem.DeprecatedAttribute,
+ JET2Messages.JET2Compiler_DeprecatedAttribute, new Object[] {attrName},
+ start.getCursor(), stop.getCursor(), start.getLine(), start.getCol());
+ }
+ }
+
+ String pkg = (String)attributes.get("package"); //$NON-NLS-1$
+ String cls = (String)attributes.get("class"); //$NON-NLS-1$
+ String importStr = (String)attributes.get("imports"); //$NON-NLS-1$
+ String startTag = (String)attributes.get("startTag"); //$NON-NLS-1$
+ String endTag = (String)attributes.get("endTag"); //$NON-NLS-1$
+
+ if(pkg != null)
+ {
+ compilationUnit.setOutputJavaPackage(pkg);
+ }
+ if(cls != null)
+ {
+ compilationUnit.setOutputJavaClassName(cls);
+ }
+ if(importStr != null)
+ {
+ String[] imports = importStr.split("\\s+"); //$NON-NLS-1$
+ compilationUnit.addImports(Arrays.asList(imports));
+ }
+
+ if(startTag != null)
+ {
+ parser.setStartTag(startTag);
+ }
+ if(endTag != null)
+ {
+ parser.setEndTag(endTag);
+ }
+ }
+
+ /**
+ * @param start
+ * @param stop
+ * @param attributes
+ */
+ private void handleTagLibDirective(JETMark start, JETMark stop, Map attributes)
+ {
+ String prefix = ((String)attributes.get(PREFIX__ATTR)).trim().toLowerCase();
+ String id = (String)attributes.get(ID__ATTR);
+
+ if (id == null)
+ {
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.MissingRequiredAttribute,
+ JET2Messages.JET2Compiler_MissingDirectiveAttribute,
+ new Object []{ TAGLIB__DIRECTIVE, ID__ATTR },
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ }
+ else if (prefix == null)
+ {
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.MissingRequiredAttribute,
+ JET2Messages.JET2Compiler_MissingDirectiveAttribute,
+ new Object []{ TAGLIB__DIRECTIVE, PREFIX__ATTR },
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ }
+ else if (!tagLibManager.canDefinePrefix(prefix, id))
+ {
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.DuplicateXMLNamespacePrefix,
+ JET2Messages.JET2Compiler_PrefixAlreadyAssigned,
+ new Object []{ prefix, tagLibManager.getLibraryIdFromPrefix(prefix) },
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ }
+ else if (tagLibManager.isLibraryDefined(id))
+ {
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.UnknownTagLibrary,
+ JET2Messages.JET2Compiler_UnknownTagLibrary,
+ new Object []{ id },
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ }
+ else
+ {
+ tagLibManager.add(prefix, id);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleExpression(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleExpression(JETMark start, JETMark stop, Map attributes)
+ {
+ JavaExpression expression = ast.newJavaExpression(
+ start.getLine(),
+ start.getCol(),
+ start.getCursor() - 3,
+ stop.getCursor() + 2,
+ start.getCursor(),
+ stop.getCursor(), reader.getChars(start, stop));
+
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(expression);
+ }
+ else
+ {
+ XMLBodyElement topElement = elementStack.peek();
+
+ topElement.addBodyElement(expression);
+ }
+
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleCharData(char[])
+ */
+ public void handleCharData(char[] chars)
+ {
+ TextElement text = ast.newTextElement(chars);
+
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(text);
+ }
+ else
+ {
+ XMLBodyElement topElement = elementStack.peek();
+
+ topElement.addBodyElement(text);
+ }
+
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#endPageProcessing()
+ */
+ public void endPageProcessing()
+ {
+ while (!elementStack.isEmpty())
+ {
+ XMLBodyElement element = elementStack.pop();
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.MissingXmlEndTag,
+ JET2Messages.JET2Compiler_MissingEndTag,
+ new Object []{ element.getName() },
+ element.getStart(),
+ element.getEnd(),
+ element.getLine(), element.getColumn());
+ }
+
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener#handleScriptlet(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleScriptlet(JETMark start, JETMark stop, Map attributes)
+ {
+ JavaScriptlet scriplet = ast.newJavaScriptlet(
+ start.getLine(),
+ start.getCol(),
+ start.getCursor() - 3,
+ stop.getCursor() + 2,
+ start.getCursor(),
+ stop.getCursor(), reader.getChars(start, stop));
+
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(scriplet);
+ }
+ else
+ {
+ XMLBodyElement topElement = elementStack.peek();
+
+ topElement.addBodyElement(scriplet);
+ }
+
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleComment(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark)
+ */
+ public void handleComment(JETMark start, JETMark stop)
+ {
+ Comment comment = ast.newComment(
+ start.getLine(),
+ start.getCol(),
+ start.getCursor() - 4,
+ stop.getCursor() + 4,
+ start.getCursor(),
+ stop.getCursor(), reader.getChars(start, stop));
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(comment);
+ }
+ else
+ {
+ XMLBodyElement topElement = elementStack.peek();
+
+ topElement.addBodyElement(comment);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleDeclaration(org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark)
+ */
+ public void handleDeclaration(JETMark start, JETMark stop)
+ {
+ JavaDeclaration decl = ast.newJavaDeclaration(
+ start.getLine(),
+ start.getCol(),
+ start.getCursor() - 3,
+ stop.getCursor() + 2,
+ start.getCursor(),
+ stop.getCursor(), reader.getChars(start, stop));
+
+ compilationUnit.addBodyElement(decl);
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleXMLEndTag(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark)
+ */
+ public void handleXMLEndTag(String tagName, JETMark start, JETMark stop)
+ {
+ int tagIndex = elementStack.findElementIndex(tagName);
+ XMLBodyElementEnd endTag = ast.newXMLBodyElementEnd(start.getLine(), start.getCol(),
+ start.getCursor(), stop.getCursor(), tagName);
+ if (tagIndex == -1)
+ {
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.MissingXmlStartTag,
+ JET2Messages.JET2Compiler_MissingStartTag,
+ new Object []{ tagName },
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ }
+ else
+ {
+ while (!elementStack.isAtTop(tagIndex))
+ {
+ XMLBodyElement top = elementStack.pop();
+ compilationUnit.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.MissingXmlEndTag,
+ JET2Messages.JET2Compiler_MissingEndTag,
+ new Object []{ top.getName() },
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ }
+ XMLBodyElement topElement = elementStack.pop();
+ endTag.setStartTag(topElement);
+ topElement.setEndTag(endTag);
+ }
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(endTag);
+ }
+ else
+ {
+ XMLBodyElement topElement = (XMLBodyElement)elementStack.peek();
+
+ topElement.addBodyElement(endTag);
+ }
+
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleXMLEmptyTag(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleXMLEmptyTag(String tagName, JETMark start, JETMark stop, Map attributeMap)
+ {
+ TagDefinition td = tagLibManager.getTagDefinition(tagName);
+
+ XMLEmptyElement decl = ast.newXMLEmptyElement(
+ start.getLine(),
+ start.getCol(),
+ start.getCursor(),
+ stop.getCursor(),
+ tagName,
+ attributeMap,
+ td);
+
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(decl);
+ }
+ else
+ {
+ XMLBodyElement topElement = (XMLBodyElement)elementStack.peek();
+
+ topElement.addBodyElement(decl);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#handleXMLStartTag(java.lang.String, org.eclipse.jet.internal.parser.JETMark, org.eclipse.jet.internal.parser.JETMark, java.util.Map)
+ */
+ public void handleXMLStartTag(String tagName, JETMark start, JETMark stop, Map attributeMap)
+ {
+
+ TagDefinition td = tagLibManager.getTagDefinition(tagName);
+
+ if(!td.isContentAllowed() && tagName.indexOf(':') == -1)
+ {
+ // bug 147714: for DPTK compatibility, allow empty tags to appear as <tagname ...> instead of <tagname .../>
+ // We only allow this for tags that have no namespace (as DPTK did not support namespaces).
+ recordProblem(ProblemSeverity.WARNING, IProblem.TagInterpretedAsEmptyTag,
+ JET2Messages.JET2Compiler_TagShouldBeEmptyFormat,
+ new Object [] {"<" + tagName + "/>"}, //$NON-NLS-1$ //$NON-NLS-2$
+ start.getCursor(), stop.getCursor(), start.getLine(), start.getCol());
+ handleXMLEmptyTag(tagName, start, stop, attributeMap);
+ }
+ else
+ {
+ XMLBodyElement decl = ast.newXMLBodyElement(
+ start.getLine(),
+ start.getCol(),
+ start.getCursor(),
+ stop.getCursor(),
+ tagName,
+ attributeMap,
+ td);
+
+ if (elementStack.isEmpty())
+ {
+ compilationUnit.addBodyElement(decl);
+ }
+ else
+ {
+ XMLBodyElement topElement = elementStack.peek();
+
+ topElement.addBodyElement(decl);
+ }
+
+ elementStack.push(decl);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#isKnownTag(java.lang.String)
+ */
+ public boolean isKnownTag(String tagName)
+ {
+ return tagLibManager.isKnownTag(tagName);
+ }
+
+ /**
+ * Return the compilation unit created as a result of handling the JET2 parser events.
+ * @return compilation unit
+ */
+ public JETCompilationUnit getCompilationUnit()
+ {
+ return compilationUnit;
+ }
+
+ /**
+ * @see org.eclipse.jet.internal.parser.JETParseEventListener2#recordProblem(org.eclipse.jet.compiler.Problem.ProblemSeverity, int, java.lang.String, java.lang.Object[], int, int, int, int)
+ */
+ public void recordProblem(ProblemSeverity severity, int problemId, String message, Object[] msgArgs, int start, int end, int line, int colOffset)
+ {
+ compilationUnit.createProblem(severity, problemId, message, msgArgs, start, end, line, colOffset);
+ }
+
+ public TagLibraryReference[] getTagLibraryReferences()
+ {
+ TagLibraryReference[] result = tagLibManager.getTagLibraryReferences();
+
+ return result;
+ }
+
+ /**
+ * @deprecated
+ * @param reader
+ */
+ public void parse(JETReader reader) {
+ parser = configureParser(reader);
+ this.beginPageProcessing();
+ try
+ {
+ parser.parse();
+ }
+ catch (org.eclipse.jet.internal.core.parser.jasper.JETException e)
+ {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e.getLocalizedMessage(), null, 0, 0, 1, 1);
+ }
+ this.endPageProcessing();
+
+ }
+
+ /**
+ * @param reader
+ * @return
+ */
+ private JETParser configureParser(JETReader reader)
+ {
+ JETParser.Directive directive = new JETParser.Directive();
+ directive.getDirectives().add("jet"); //$NON-NLS-1$
+ directive.getDirectives().add("taglib"); //$NON-NLS-1$
+ directive.getDirectives().add("include"); //$NON-NLS-1$
+ directive.getDirectives().add("start"); //$NON-NLS-1$
+ directive.getDirectives().add("end"); //$NON-NLS-1$
+ JETCoreElement[] coreElements = new JETCoreElement []{
+ new ErrorRedirectingCoreElementDelegate(directive),
+ new ErrorRedirectingCoreElementDelegate(new JETParser.Expression()),
+ new ErrorRedirectingCoreElementDelegate(new CommentElementDelegate()),
+ new ErrorRedirectingCoreElementDelegate(new DeclarationElementDelegate()),
+ new ErrorRedirectingCoreElementDelegate(new JETParser.Scriptlet()),
+ new ErrorRedirectingCoreElementDelegate(new XMLElementDelegate()), };
+
+ return new JETParser(reader, this, coreElements);
+ }
+
+ public boolean isKnownInvalidTagName(String tagName)
+ {
+ return tagLibManager.isKnownInvalidTagName(tagName);
+ }
+
+ public Object parse(String templatePath)
+ {
+ final ITemplateInput templateInput = templateResolver.getInput(templatePath);
+ final URI baseLocation = templateInput.getBaseLocation();
+ try
+ {
+ if(compilationUnit == null) {
+ compilationUnit = new JETAST().newJETCompilationUnit(baseLocation, templatePath, templateInput.getEncoding());
+ ast = compilationUnit.getAst();
+ }
+ reader = new JETReader(baseLocation == null ? null : baseLocation.toString(), templatePath, templateInput.getReader());
+ intermalParse();
+ }
+ catch (org.eclipse.jet.internal.core.parser.jasper.JETException e)
+ {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e.getLocalizedMessage(), null, 0, 0, 1, 1);
+ }
+ catch (TemplateInputException e)
+ {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e.getLocalizedMessage(), null, 0, 0, 1, 1);
+ }
+ return compilationUnit;
+ }
+
+ public Object parse(char[] template)
+ {
+ if(compilationUnit == null) {
+ compilationUnit = new JETAST().newJETCompilationUnit(null, "", null); //$NON-NLS-1$
+ ast = compilationUnit.getAst();
+ }
+ try
+ {
+ reader = new JETReader(null, "", new CharArrayReader(template)); //$NON-NLS-1$
+ intermalParse();
+ }
+ catch (JETException e)
+ {
+ // create a minimal compilation unit with the exeception recorded as the error.
+ recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e.getLocalizedMessage(), null, 0, 0, 1, 1);
+ }
+ return compilationUnit;
+ }
+
+ /**
+ * @throws JETException
+ */
+ private void intermalParse() throws JETException
+ {
+ parser = configureParser(reader);
+ this.beginPageProcessing();
+ parser.parse();
+ this.endPageProcessing();
+ compilationUnit.setTagLibraryReferences(getTagLibraryReferences());
+ compilationUnit.accept(new TagValidationVisitor(compilationUnit));
+ compilationUnit.accept(new TextTrimmingVisitor());
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/LineInfo.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/LineInfo.java
new file mode 100644
index 0000000..d5c345d
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/LineInfo.java
@@ -0,0 +1,150 @@
+package org.eclipse.jet.internal.core.parser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Represent line information. Each line has a buffer relative offset of its first character (start), and a buffer
+ * relative offset of its line separator (end).
+ */
+public final class LineInfo {
+ private final int start;
+ private final int end;
+ private final String delimiter;
+
+ public LineInfo(int start, int end, String delimiter)
+ {
+ this.start = start;
+ this.end = end;
+ this.delimiter = delimiter;
+
+ }
+
+ /**
+ * @return Returns the delimiter.
+ */
+ public final String getDelimiter()
+ {
+ return delimiter;
+ }
+
+ /**
+ * @return Returns the end.
+ */
+ public final int getEnd()
+ {
+ return end;
+ }
+
+ /**
+ * @return Returns the start.
+ */
+ public final int getStart()
+ {
+ return start;
+ }
+
+ public String toString()
+ {
+ return "[" + start + ", " + end + ") "; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ }
+
+ /**
+ * Return an array of Line info objects describing the lines of the passed buffer. The
+ * code recognizes the following line separators:
+ * <bl>
+ * <li>\r - MAC/OS</li>
+ * <li>\n - Unix/Linux</li>
+ * <li>\r\n - Windows</li>
+ * </bl>
+ * If the last line lacks a separater, then an empty separator is assumed.
+ * @param buffer the buffer to analyse.
+ * @return an array of LineInfo objects. The array will be empty if <code>buffer</code> is empty.
+ */
+ public static LineInfo[] calculateLines(char[] buffer)
+ {
+ List list = new ArrayList();
+
+
+ int start = 0;
+ String separator;
+ for (int i = 0; i < buffer.length; i++)
+ {
+ switch(buffer[i])
+ {
+ case '\r':
+ if(i + 1 < buffer.length && buffer[i+1] == '\n') {
+ separator = "\r\n"; //$NON-NLS-1$
+ } else {
+ separator = "\r"; //$NON-NLS-1$
+ }
+ break;
+ case '\n':
+ separator = "\n"; //$NON-NLS-1$
+ break;
+ default:
+ continue;
+ }
+ LineInfo lineInfo = new LineInfo(start, i, separator);
+ list.add(lineInfo);
+ if(separator.length() == 2) {
+ i++;
+ }
+ start = i + 1;
+ }
+ if(start < buffer.length) {
+ LineInfo lineInfo = new LineInfo(start, buffer.length, ""); //$NON-NLS-1$
+ list.add(lineInfo);
+ }
+
+ return (LineInfo[])list.toArray(new LineInfo[list.size()]);
+ }
+
+ /**
+ * Return the LineInfo for the given offset
+ * @param lineInfo an array of LineInfo objects, as returned by {@link #calculateLines(char[])}.
+ * @param offset an offset in the file.
+ * @return the line index.
+ */
+ public static LineInfo getLineInfo(LineInfo[] lineInfo, final int offset)
+ {
+ final int index = getLineNo(lineInfo, offset) - 1;
+ return index < lineInfo.length ? lineInfo[index] : null;
+ }
+
+ /**
+ * Return the line number of a given offset.
+ * @param lineInfo
+ * @param offset
+ * @return the one based line number
+ */
+ public static int getLineNo(LineInfo[] lineInfo, final int offset)
+ {
+ if(offset < 0) {
+ throw new IllegalArgumentException("offset = " + offset); //$NON-NLS-1$
+ }
+ final int index = Arrays.binarySearch(lineInfo, null, new Comparator() {
+
+ public int compare(Object arg0, Object arg1)
+ {
+ LineInfo li = (LineInfo)arg0;
+ if( offset < li.start) {
+ return 1;
+ }
+ if(li.start <= offset && offset < li.end + li.delimiter.length()) {
+ return 0;
+ }
+ return -1;
+ }});
+ return (index >= 0 ? index : -index - 1) + 1;
+ }
+
+ public boolean hasDelimiter()
+ {
+ return getDelimiter().length() > 0;
+ }
+
+
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TagValidationVisitor.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TagValidationVisitor.java
new file mode 100644
index 0000000..40cb02b
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TagValidationVisitor.java
@@ -0,0 +1,172 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagValidationVisitor.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.internal.core.parser;
+
+
+import java.util.Iterator;
+
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+import org.eclipse.jet.core.parser.ast.JETASTVisitor;
+import org.eclipse.jet.core.parser.ast.JETCompilationUnit;
+import org.eclipse.jet.core.parser.ast.XMLBodyElement;
+import org.eclipse.jet.core.parser.ast.XMLElement;
+import org.eclipse.jet.core.parser.ast.XMLEmptyElement;
+import org.eclipse.jet.internal.l10n.JET2Messages;
+import org.eclipse.jet.taglib.TagAttributeDefinition;
+import org.eclipse.jet.taglib.TagDefinition;
+
+
+/**
+ * Validate tags and attributes found on tag declarations.
+ * <p>
+ * Types of attribute validation:
+ * <bl>
+ * <li>that required attributes are in the tag declaration.</li>
+ * <li>that all attributes correspond to a declared attribute.</li>
+ * <li>that warnings are generated for deprecated attribute access.</li>
+ * </bl>
+ * <p>
+ * Types of tag validation:
+ * <bl>
+ * <li>whether tag or tag library is deprecated</li>
+ * <li>whether the tag is permitted to be used as an empty tag</li>
+ * <li>whether the tag is permitted to be used as a content tag</li>
+ * </bl>
+ * </p>
+ */
+public final class TagValidationVisitor extends JETASTVisitor
+{
+
+ private final JETCompilationUnit cu;
+
+ /**
+ * @param cu
+ *
+ */
+ public TagValidationVisitor(JETCompilationUnit cu)
+ {
+ super();
+ this.cu = cu;
+ }
+
+ public boolean visit(XMLBodyElement xmlBodyElement)
+ {
+ if (!xmlBodyElement.getTagDefinition().isContentAllowed())
+ {
+ cu.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.TagCannotHaveContent,
+ JET2Messages.JET2Compiler_TagCannotHaveContent,
+ new Object [0],
+ xmlBodyElement.getStart(),
+ xmlBodyElement.getEnd(),
+ xmlBodyElement.getLine(),
+ xmlBodyElement.getColumn());
+ }
+ validateAttributes(xmlBodyElement);
+ return true;
+ }
+
+ private void validateAttributes(XMLElement xmlElement)
+ {
+ TagDefinition tagDefinition = xmlElement.getTagDefinition();
+
+ if (tagDefinition.isDeprecated() || tagDefinition.getTagLibrary().isDeprecated())
+ {
+ cu.createProblem(
+ ProblemSeverity.WARNING,
+ IProblem.DeprecatedTag,
+ JET2Messages.JET2Compiler_DeprecatedTag,
+ new Object [0],
+ xmlElement.getStart(),
+ xmlElement.getEnd(),
+ xmlElement.getLine(),
+ xmlElement.getColumn());
+ }
+
+ // Validate that every supplied attribute is in the tag definition
+ for (Iterator i = xmlElement.getAttributes().keySet().iterator(); i.hasNext();)
+ {
+ String attrName = (String)i.next();
+ if (tagDefinition.getAttributeDefinition(attrName) == null)
+ {
+ cu.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.UnknownAttributeInTag,
+ JET2Messages.JET2Compiler_UnknownAttribute,
+ new Object []{ attrName },
+ xmlElement.getStart(),
+ xmlElement.getEnd(),
+ xmlElement.getLine(),
+ xmlElement.getColumn());
+ }
+ }
+
+ // Validate that every required attribute is in the supplied attributes
+ for (Iterator i = tagDefinition.getAttributeDefinitions().iterator(); i.hasNext();)
+ {
+ TagAttributeDefinition tad = (TagAttributeDefinition)i.next();
+ final String attributeName = tad.getName();
+ if (tad.isRequired() && !xmlElement.getAttributes().containsKey(attributeName))
+ {
+ cu.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.MissingRequiredAttribute,
+ JET2Messages.JET2Compiler_MissingRequiredAttribute,
+ new Object []{ attributeName },
+ xmlElement.getStart(),
+ xmlElement.getEnd(),
+ xmlElement.getLine(),
+ xmlElement.getColumn());
+ }
+ if (tad.isDeprecated() && xmlElement.getAttributes().containsKey(attributeName))
+ {
+ cu.createProblem(
+ ProblemSeverity.WARNING,
+ IProblem.DeprecatedAttribute,
+ JET2Messages.JET2Compiler_DeprecatedAttribute,
+ new Object []{ attributeName },
+ xmlElement.getStart(),
+ xmlElement.getEnd(),
+ xmlElement.getLine(),
+ xmlElement.getColumn());
+ }
+ }
+ }
+
+ public boolean visit(XMLEmptyElement xmlEmptyElement)
+ {
+ if (!xmlEmptyElement.getTagDefinition().isEmptyTagAllowed())
+ {
+ cu.createProblem(
+ ProblemSeverity.ERROR,
+ IProblem.TagCannotBeEmpty,
+ JET2Messages.JET2Compiler_TagRequiresContent,
+ new Object [0],
+ xmlEmptyElement.getStart(),
+ xmlEmptyElement.getEnd(),
+ xmlEmptyElement.getLine(),
+ xmlEmptyElement.getColumn());
+ }
+ validateAttributes(xmlEmptyElement);
+
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TextTrimmingVisitor.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TextTrimmingVisitor.java
new file mode 100644
index 0000000..59c4975
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/TextTrimmingVisitor.java
@@ -0,0 +1,181 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TextTrimmingVisitor.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.internal.core.parser;
+
+
+import org.eclipse.jet.core.parser.ast.Comment;
+import org.eclipse.jet.core.parser.ast.JETASTElement;
+import org.eclipse.jet.core.parser.ast.JETASTVisitor;
+import org.eclipse.jet.core.parser.ast.JETDirective;
+import org.eclipse.jet.core.parser.ast.JavaDeclaration;
+import org.eclipse.jet.core.parser.ast.JavaExpression;
+import org.eclipse.jet.core.parser.ast.JavaScriptlet;
+import org.eclipse.jet.core.parser.ast.TextElement;
+import org.eclipse.jet.core.parser.ast.XMLBodyElement;
+import org.eclipse.jet.core.parser.ast.XMLBodyElementEnd;
+import org.eclipse.jet.core.parser.ast.XMLEmptyElement;
+
+
+/**
+ * A JET AST Visitor that strips whitespace and new-lines
+ *
+ */
+public class TextTrimmingVisitor extends JETASTVisitor
+{
+ /**
+ *
+ */
+ public TextTrimmingVisitor()
+ {
+ super();
+ }
+
+ private void checkAndStrip(JETASTElement element)
+ {
+ if(element.removeLineWhenOtherwiseEmpty())
+ {
+ JETASTElement prev = element.getPrevElement();
+ JETASTElement next = element.getNextElement();
+ if(endsWithEmtpyLine(prev) && startsWithEmptyLine(next))
+ {
+ trimLastLine(prev);
+ trimFirstLine(next);
+ }
+ }
+ }
+
+ private void trimLastLine(JETASTElement element)
+ {
+ if(element instanceof TextElement)
+ {
+ TextElement text = (TextElement)element;
+ text.setTrimLastLine(true);
+ }
+ }
+
+ private void trimFirstLine(JETASTElement element)
+ {
+ if(element instanceof TextElement)
+ {
+ TextElement text = (TextElement)element;
+ text.setTrimFirstLine(true);
+ }
+ }
+
+ private boolean startsWithEmptyLine(JETASTElement element)
+ {
+ if(element instanceof TextElement)
+ {
+ TextElement text = (TextElement)element;
+ final LineInfo[] lines = text.getLines();
+ if(lines.length > 0 && lines[0].hasDelimiter())
+ {
+ LineInfo line = lines[0];
+ if(new String(text.getRawText(), line.getStart(), line.getEnd() - line.getStart()).trim().length() == 0)
+ {
+ // first line is all whitespace
+ return true;
+ }
+ }
+ }
+ else if(element == null)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean endsWithEmtpyLine(JETASTElement element)
+ {
+ if(element instanceof TextElement)
+ {
+ TextElement text = (TextElement)element;
+ final LineInfo[] lines = text.getLines();
+ if(lines.length > 0)
+ {
+ LineInfo line = lines[lines.length - 1];
+ if(line.hasDelimiter()) {
+ // last line has a delimiter => this element start on col # 1
+ return true;
+ }
+ else if( lines.length > 1 && new String(text.getRawText(), line.getStart(), line.getEnd() - line.getStart()).trim().length() == 0)
+ {
+ // last line is all whitespace
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if(element == null)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean visit(JavaDeclaration declaration)
+ {
+ checkAndStrip(declaration);
+ return true;
+ }
+
+ public boolean visit(JETDirective directive)
+ {
+ checkAndStrip(directive);
+ return true;
+ }
+
+ public boolean visit(JavaExpression expression)
+ {
+ checkAndStrip(expression);
+ return true;
+ }
+
+ public boolean visit(JavaScriptlet scriptlet)
+ {
+ checkAndStrip(scriptlet);
+ return true;
+ }
+
+ public boolean visit(XMLEmptyElement xmlEmptyElement)
+ {
+ checkAndStrip(xmlEmptyElement);
+ return true;
+ }
+
+ public boolean visit(XMLBodyElement xmlBodyElement)
+ {
+ checkAndStrip(xmlBodyElement);
+ return true;
+ }
+
+ public boolean visit(XMLBodyElementEnd xmlBodyElementEnd)
+ {
+ checkAndStrip(xmlBodyElementEnd);
+ return true;
+ }
+
+ public boolean visit(Comment comment)
+ {
+ checkAndStrip(comment);
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/CommentElementDelegate.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/CommentElementDelegate.java
new file mode 100644
index 0000000..c890a87
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/CommentElementDelegate.java
@@ -0,0 +1,77 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: CommentElementDelegate.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+
+package org.eclipse.jet.internal.core.parser.jasper;
+
+/**
+ * A JET Compiler delegate for handling JET2 comments (&lt;%-- ... --%&gt;).
+ *
+ */
+public class CommentElementDelegate implements JETCoreElement
+{
+
+ private static final String STD_COMMENT_CHARS = "--"; //$NON-NLS-1$
+
+ /**
+ * Create a new CommentElementDelegate
+ */
+ public CommentElementDelegate()
+ {
+ super();
+ }
+
+ /**
+ * Given the parser state (reader, parser), determine if the parser is currently located at
+ * a JET2 comment. If so, parse it, calling appropriate methods on <code>listener</code>.
+ * @param listener the JET parser event listener, to which comment events are dispatched if a comment is recognized
+ * @param reader the JET reader (lexer)
+ * @param parser the JET parser instance
+ * @return <code>true</code> a JET2 comment element was recognized, <code>false</code> otherwise
+ * @throws JETException if an error occurs
+ *
+ */
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ if (!(listener instanceof JETParseEventListener2))
+ {
+ return false;
+ }
+ JETParseEventListener2 jet2Listener = (JETParseEventListener2)listener;
+
+ String elementOpen = parser.getOpenScriptlet() + STD_COMMENT_CHARS;
+ String elementClose = STD_COMMENT_CHARS + parser.getCloseScriptlet();
+ if (!reader.matches(elementOpen))
+ {
+ return false;
+ }
+ JETMark elementStart = reader.mark();
+ reader.advance(elementOpen.length());
+
+ JETMark start = reader.mark();
+ JETMark stop = reader.skipUntil(elementClose);
+ if (stop == null)
+ {
+ MessagesUtil.recordUnterminatedElement(jet2Listener, elementClose, elementStart, reader);
+ }
+ else
+ {
+ jet2Listener.handleComment(start, stop);
+ }
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/DeclarationElementDelegate.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/DeclarationElementDelegate.java
new file mode 100644
index 0000000..b1d69be
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/DeclarationElementDelegate.java
@@ -0,0 +1,63 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: DeclarationElementDelegate.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+/**
+ * Parser delegate for parsing a JET2 declaration &lt;%! ... %&gt;
+ */
+public class DeclarationElementDelegate implements JETCoreElement
+{
+
+ private static final String STD_DECL_CHARS = "!"; //$NON-NLS-1$
+
+ public DeclarationElementDelegate()
+ {
+ super();
+ }
+
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ if (!(listener instanceof JETParseEventListener2))
+ {
+ return false;
+ }
+ JETParseEventListener2 jet2Listener = (JETParseEventListener2)listener;
+
+ String declOpen = parser.getOpenScriptlet() + STD_DECL_CHARS;
+ String declClose = parser.getCloseScriptlet();
+ if (!reader.matches(declOpen))
+ {
+ return false;
+ }
+ JETMark elementStart = reader.mark();
+ reader.advance(declOpen.length());
+
+ JETMark start = reader.mark();
+ JETMark stop = reader.skipUntil(declClose);
+ if (stop == null)
+ {
+ MessagesUtil.recordUnterminatedElement(jet2Listener, declClose, elementStart, reader);
+ }
+ else
+ {
+ jet2Listener.handleDeclaration(start, stop);
+ }
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/ErrorRedirectingCoreElementDelegate.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/ErrorRedirectingCoreElementDelegate.java
new file mode 100644
index 0000000..0683abb
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/ErrorRedirectingCoreElementDelegate.java
@@ -0,0 +1,56 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: ErrorRedirectingCoreElementDelegate.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+
+/**
+ * A JETCoreElement that delegates its accept the the passed core element, but records any JETException
+ * as errors on a JETParseEventListener2.
+ */
+public class ErrorRedirectingCoreElementDelegate implements JETCoreElement
+{
+
+ private final JETCoreElement delegate;
+
+ public ErrorRedirectingCoreElementDelegate(JETCoreElement delegate) {
+ this.delegate = delegate;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.internal.parser.JETCoreElement#accept(org.eclipse.jet.internal.parser.JETParseEventListener, org.eclipse.jet.internal.parser.JETReader, org.eclipse.jet.internal.parser.JETParser)
+ */
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser)
+ {
+ boolean isAccepted = true;
+ JETMark start = reader.mark();
+ try {
+ isAccepted = delegate.accept(listener, reader, parser);
+ }
+ catch(JETException e) {
+ if(listener instanceof JETParseEventListener2)
+ {
+ JETParseEventListener2 listener2 = (JETParseEventListener2)listener;
+ listener2.recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e.getLocalizedMessage(), new Object[0],
+ start.getCursor(), reader.mark().getCursor(), start.getLine(), start.getCol());
+ }
+ }
+ return isAccepted;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETCoreElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETCoreElement.java
new file mode 100644
index 0000000..bb1080d
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETCoreElement.java
@@ -0,0 +1,30 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 2002, 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETCoreElement.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+/**
+ * The core elements we recognize;
+ * these are stateless abstractions that represent the parsing action for a core tag.
+ */
+public interface JETCoreElement
+{
+ /**
+ * Return true if the input contained the sequence that matched
+ * the action corresponding to this core tag.
+ */
+ boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException;
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETException.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETException.java
new file mode 100644
index 0000000..bd9533a
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 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.jet.internal.core.parser.jasper;
+
+/**
+ * Base class for all exceptions generated by the JET engine.
+ * Makes it convienient to catch just this at the top-level.
+ */
+public class JETException extends Exception {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -9117189748450969429L;
+
+ public JETException(String reason) {
+ super(reason);
+ }
+
+ /**
+ * Creates a JETException with the embedded exception and the reason for throwing a JETException.
+ */
+ public JETException(String reason, Throwable exception) {
+ super(reason, exception);
+ }
+
+ /**
+ * Creates a JETException with the embedded exception.
+ */
+ public JETException(Throwable exception) {
+ super(exception);
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETMark.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETMark.java
new file mode 100644
index 0000000..e5164bd
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETMark.java
@@ -0,0 +1,268 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 2002, 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETMark.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+import java.util.Stack;
+
+import org.eclipse.jet.internal.core.parser.LineInfo;
+
+
+/**
+ * A mark represents a point in the JET input.
+ */
+public final class JETMark
+{
+
+ /**
+ * This is the character offset.
+ */
+ protected int cursor;
+
+ /**
+ * This is the id of the file.
+ */
+ protected int fileid;
+
+ /**
+ * This is the base URI for relative paths.
+ */
+ protected String baseDir;
+
+ /**
+ * This is the stream of characters.
+ */
+ protected char[] stream = null;
+
+ /**
+ * This is an array of lines descriptors.
+ */
+ protected LineInfo[] lineInfo = null;
+
+ /**
+ * This is the stack of inclusions.
+ */
+ protected Stack includeStack = null;
+
+ /**
+ * This is the reader that owns this mark.
+ */
+ protected JETReader reader;
+
+ /**
+ * Keep track of parser before parsing an included file.
+ * This class keeps track of the parser before we switch to parsing an
+ * included file. In other words, it's the parser's continuation to be
+ * reinstalled after the included file parsing is done.
+ */
+ class IncludeState
+ {
+ int cursor;
+
+ int fileid;
+
+ String baseDir;
+
+ String encoding;
+
+ char[] stream = null;
+
+ private final LineInfo[] lineInfo;
+
+ IncludeState(int inCursor, LineInfo[] inLineInfo, int inFileid, String inBaseDir, char[] inStream)
+ {
+ cursor = inCursor;
+ lineInfo = inLineInfo;
+ fileid = inFileid;
+ baseDir = inBaseDir;
+ stream = inStream;
+ }
+ }
+
+ /**
+ * Creates a new mark
+ * @param reader JETReader this mark belongs to
+ * @param inStream current stream for this mark
+ * @param fileid id of requested jet file
+ * @param inBaseDir base directory of requested jet file
+ */
+ JETMark(JETReader reader, char[] inStream, int fileid, String inBaseDir)
+ {
+ this.reader = reader;
+ this.stream = inStream;
+ this.cursor = 0;
+ this.lineInfo = LineInfo.calculateLines(inStream);
+ this.fileid = fileid;
+ this.baseDir = inBaseDir;
+ this.includeStack = new Stack();
+ }
+
+ JETMark(JETMark other)
+ {
+ this.reader = other.reader;
+ this.stream = other.stream;
+ this.fileid = other.fileid;
+ this.cursor = other.cursor;
+ this.lineInfo = other.lineInfo;
+ this.baseDir = other.baseDir;
+
+ // clone includeStack without cloning contents
+ //
+ includeStack = new Stack();
+ for (int i = 0; i < other.includeStack.size(); ++i)
+ {
+ includeStack.addElement(other.includeStack.elementAt(i));
+ }
+ }
+
+ /**
+ * Sets this mark's state to a new stream.
+ * It will store the current stream in it's includeStack.
+ * @param inStream new stream for mark
+ * @param inFileid id of new file from which stream comes from
+ * @param inBaseDir directory of file
+ * @param inEncoding encoding of new file
+ */
+ public void pushStream(char[] inStream, int inFileid, String inBaseDir)
+ {
+ // Store the current state in stack.
+ //
+ includeStack.push(new IncludeState(cursor, lineInfo, fileid, baseDir, stream));
+
+ // Set the new variables.
+ //
+ cursor = 0;
+ fileid = inFileid;
+ baseDir = inBaseDir;
+ stream = inStream;
+ lineInfo = LineInfo.calculateLines(inStream);
+ }
+
+ /**
+ * Restores this mark's state to a previously stored stream.
+ */
+ public boolean popStream()
+ {
+ // Make sure we have something to pop.
+ //
+ if (includeStack.size() <= 0)
+ {
+ return false;
+ }
+
+ // Get previous state in stack.
+ //
+ IncludeState state = (IncludeState)includeStack.pop();
+
+ // Set the new variables.
+ //
+ cursor = state.cursor;
+ lineInfo = state.lineInfo;
+ fileid = state.fileid;
+ baseDir = state.baseDir;
+ stream = state.stream;
+ return true;
+ }
+
+ public String getFile()
+ {
+ return reader.getFile(fileid);
+ }
+
+ public String getBaseURI()
+ {
+ return reader.getBaseURI(fileid);
+ }
+
+ public String getLocalFile()
+ {
+ String file = reader.getFile(fileid);
+ if(file.startsWith("platform:/resource/")) { //$NON-NLS-1$
+ file = file.substring("platform:/resource".length()); //$NON-NLS-1$
+ }
+ else if (file.startsWith("file:/")) //$NON-NLS-1$
+ {
+ // FIXME delegate this to an ITemplateResolver
+// IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+// IFile iFile = workspaceRoot.getFileForLocation(new Path(file.substring(6)));
+// file = iFile.getFullPath().toString();
+ }
+
+ return file;
+ }
+
+ public int getFileId()
+ {
+ return fileid;
+ }
+
+ public int getCursor()
+ {
+ return cursor;
+ }
+
+ public String toShortString()
+ {
+ return "(" + getLine() + "," + getCol() + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
+ }
+
+ public String toString()
+ {
+ return getLocalFile() + "(" + getLine() + "," + getCol() + ")"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ public String format(String key)
+ {
+ return MessagesUtil.getString(
+ key,
+ new Object []{ getLocalFile(), new Integer(getLine() + 1), new Integer(getCol() + 1), new Integer(cursor) });
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other instanceof JETMark)
+ {
+ JETMark m = (JETMark)other;
+ return this.reader == m.reader && this.fileid == m.fileid && this.cursor == m.cursor && this.getLine() == m.getLine()
+ && this.getCol() == m.getCol();
+ }
+ return false;
+ }
+
+ /**
+ * Return the one-based line number of the cursor.
+ * @return Returns the line.
+ */
+ public int getLine()
+ {
+ return LineInfo.getLineNo(lineInfo, cursor);
+ }
+
+ /**
+ * Return the one-based column number of the cursor.
+ * @return the one based column number of the cursor.
+ */
+ public int getCol()
+ {
+ int lineIndex = getLine() - 1;
+ if(lineIndex < lineInfo.length) {
+ return cursor - lineInfo[lineIndex].getStart() + 1;
+ } else {
+ return cursor - (lineInfo[lineInfo.length - 1].getEnd() + lineInfo[lineInfo.length - 1].getDelimiter().length()) + 1;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener.java
new file mode 100644
index 0000000..7a29fd9
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener.java
@@ -0,0 +1,39 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 2002, 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETParseEventListener.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+import java.util.Map;
+
+
+/**
+ * The interface for the JET code generation backend.
+ */
+public interface JETParseEventListener
+{
+ void beginPageProcessing() throws JETException;
+
+ void handleDirective(String directive, JETMark start, JETMark stop, Map attributes) throws JETException;
+
+ void handleExpression(JETMark start, JETMark stop, Map attributes) throws JETException;
+
+ void handleCharData(char[] chars) throws JETException;
+
+ void endPageProcessing() throws JETException;
+
+ void handleScriptlet(JETMark start, JETMark stop, Map attributes) throws JETException;
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener2.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener2.java
new file mode 100644
index 0000000..7a9f546
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParseEventListener2.java
@@ -0,0 +1,40 @@
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.ProblemSeverity;
+
+
+public interface JETParseEventListener2 extends JETParseEventListener
+{
+
+ void handleComment(JETMark start, JETMark stop) throws JETException;
+
+ void handleDeclaration(JETMark start, JETMark stop) throws JETException;
+
+ void handleXMLEndTag(String tagName, JETMark start, JETMark stop) throws JETException;
+
+ void handleXMLEmptyTag(String tagName, JETMark start, JETMark stop, Map attributeMap) throws JETException;
+
+ void handleXMLStartTag(String tagName, JETMark start, JETMark stop, Map attributeMap) throws JETException;
+
+ // void handleText(JETMark start, JETMark end);
+
+ boolean isKnownTag(String tagName);
+
+ boolean isKnownInvalidTagName(String tagName);
+ /**
+ * Record a parsing problem.
+ * @param severity
+ * @param problemId
+ * @param message
+ * @param msgArgs
+ * @param start
+ * @param end
+ * @param line
+ * @param colOffset TODO
+ */
+ void recordProblem(ProblemSeverity severity, int problemId, String message, Object[] msgArgs, int start, int end, int line, int colOffset);
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParser.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParser.java
new file mode 100644
index 0000000..172a282
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETParser.java
@@ -0,0 +1,643 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 2002, 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETParser.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+
+
+/**
+ * This class and all those in this package is work derived from contributions of multiple authors as listed below.
+ * Credit for all that is good is shared, responsibility for any problems lies solely with the latest authors.
+ *
+ * @author Anil K. Vijendran
+ * @author Anselm Baird-Smith
+ * @author David Charboneau
+ * @author Harish Prabandham
+ * @author Kevin Bauer
+ * @author Mandar Raje
+ * @author Paul R. Hoffman
+ * @author Rajiv Mordani
+ */
+public class JETParser implements JETReader.IStackPopNotifier
+{
+ public interface Action
+ {
+ void execute() throws JETException;
+ }
+
+ public static class DelegatingListener implements JETParseEventListener2
+ {
+ protected JETParseEventListener delegate;
+
+ protected JETParser.Action action;
+
+ public DelegatingListener(JETParseEventListener delegate, JETParser.Action action)
+ {
+ this.delegate = delegate;
+ this.action = action;
+ }
+
+ public void doAction() throws JETException
+ {
+ action.execute();
+ }
+
+ public void beginPageProcessing() throws JETException
+ {
+ delegate.beginPageProcessing();
+ }
+
+ public void endPageProcessing() throws JETException
+ {
+ action.execute();
+ delegate.endPageProcessing();
+ }
+
+ public void handleDirective(String directive, JETMark start, JETMark stop, Map attrs) throws JETException
+ {
+ doAction();
+ delegate.handleDirective(directive, start, stop, attrs);
+ }
+
+ public void handleScriptlet(JETMark start, JETMark stop, Map attrs) throws JETException
+ {
+ doAction();
+ delegate.handleScriptlet(start, stop, attrs);
+ }
+
+ public void handleExpression(JETMark start, JETMark stop, Map attrs) throws JETException
+ {
+ doAction();
+ delegate.handleExpression(start, stop, attrs);
+ }
+
+ public void handleCharData(char[] chars) throws JETException
+ {
+ delegate.handleCharData(chars);
+ }
+
+ public void handleComment(JETMark start, JETMark stop) throws JETException
+ {
+ doAction();
+ if (delegate instanceof JETParseEventListener2)
+ {
+ ((JETParseEventListener2)delegate).handleComment(start, stop);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void handleDeclaration(JETMark start, JETMark stop) throws JETException
+ {
+ doAction();
+ if (delegate instanceof JETParseEventListener2)
+ {
+ ((JETParseEventListener2)delegate).handleDeclaration(start, stop);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void handleXMLEndTag(String tagName, JETMark start, JETMark stop) throws JETException
+ {
+ doAction();
+ if (delegate instanceof JETParseEventListener2)
+ {
+ ((JETParseEventListener2)delegate).handleXMLEndTag(tagName, start, stop);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void handleXMLEmptyTag(String tagName, JETMark start, JETMark stop, Map attributeMap) throws JETException
+ {
+ doAction();
+ if (delegate instanceof JETParseEventListener2)
+ {
+ ((JETParseEventListener2)delegate).handleXMLEmptyTag(tagName, start, stop, attributeMap);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void handleXMLStartTag(String tagName, JETMark start, JETMark stop, Map attributeMap) throws JETException
+ {
+ doAction();
+ if (delegate instanceof JETParseEventListener2)
+ {
+ ((JETParseEventListener2)delegate).handleXMLStartTag(tagName, start, stop, attributeMap);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public boolean isKnownTag(String tagName)
+ {
+ if (delegate instanceof JETParseEventListener2)
+ {
+ return ((JETParseEventListener2)delegate).isKnownTag(tagName);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public void recordProblem(ProblemSeverity severity, int problemId, String message, Object[] msgArgs, int start, int end, int line, int colOffset)
+ {
+ if (delegate instanceof JETParseEventListener2)
+ {
+ ((JETParseEventListener2)delegate).recordProblem(severity, problemId, message, msgArgs, start, end, line, colOffset);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public boolean isKnownInvalidTagName(String tagName)
+ {
+ if (delegate instanceof JETParseEventListener2)
+ {
+ return ((JETParseEventListener2)delegate).isKnownInvalidTagName(tagName);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+ /**
+ * The input source we read from...
+ */
+ protected JETReader reader;
+
+ /**
+ * The backend that is notified of constructs recognized in the input...
+ */
+ protected JETParseEventListener listener;
+
+ /*
+ * Char buffer for HTML data
+ */
+ protected CharArrayWriter writer;
+
+ protected List coreElements = new ArrayList();
+
+ protected String openDirective = "<%@"; //$NON-NLS-1$
+
+ protected String closeDirective = "%>"; //$NON-NLS-1$
+
+ protected String openScriptlet = "<%"; //$NON-NLS-1$
+
+ protected String closeScriptlet = "%>"; //$NON-NLS-1$
+
+ protected String openExpr = "<%="; //$NON-NLS-1$
+
+ protected String closeExpr = "%>"; //$NON-NLS-1$
+
+ protected String quotedStartTag = "<\\%"; //$NON-NLS-1$
+
+ protected String quotedEndTag = "%\\>"; //$NON-NLS-1$
+
+ protected String startTag = "<%"; //$NON-NLS-1$
+
+ protected String endTag = "%>"; //$NON-NLS-1$
+
+ public JETParser(JETReader reader, final JETParseEventListener parseEventListener, JETCoreElement[] coreElements)
+ {
+ this.reader = reader;
+ reader.setStackPopNotifier(this);
+ this.listener = new DelegatingListener(parseEventListener, new Action()
+ {
+ public void execute() throws JETException
+ {
+ JETParser.this.flushCharData();
+ }
+ });
+
+ this.writer = new CharArrayWriter();
+
+ for (int i = 0; i < coreElements.length; i++)
+ {
+ this.coreElements.add(coreElements[i]);
+ }
+ }
+
+ public JETReader getReader()
+ {
+ return reader;
+ }
+
+ public void setStartTag(String tag)
+ {
+ openScriptlet = tag;
+ openExpr = tag + "="; //$NON-NLS-1$
+ openDirective = tag + "@"; //$NON-NLS-1$
+ quotedStartTag = tag.charAt(0) + "\\" + tag.charAt(1); //$NON-NLS-1$
+ startTag = tag;
+ reader.setStartTag(tag);
+ }
+
+ public void setEndTag(String tag)
+ {
+ closeScriptlet = tag;
+ closeExpr = tag;
+ closeDirective = tag;
+ quotedEndTag = tag.charAt(0) + "\\" + tag.charAt(1); //$NON-NLS-1$
+ endTag = tag;
+ reader.setEndTag(tag);
+ }
+
+ public String getOpenScriptlet()
+ {
+ return openScriptlet;
+ }
+
+ public String getCloseScriptlet()
+ {
+ return closeScriptlet;
+ }
+
+ public String getOpenExpr()
+ {
+ return openExpr;
+ }
+
+ public String getCloseExpr()
+ {
+ return closeExpr;
+ }
+
+ public String getOpenDirective()
+ {
+ return openDirective;
+ }
+
+ public String getCloseDirective()
+ {
+ return closeDirective;
+ }
+
+ public String getQuotedStartTag()
+ {
+ return quotedStartTag;
+ }
+
+ public String getQuotedEndTag()
+ {
+ return quotedEndTag;
+ }
+
+ public String getStartTag()
+ {
+ return startTag;
+ }
+
+ public String getEndTag()
+ {
+ return endTag;
+ }
+
+ public static class Scriptlet implements JETCoreElement
+ {
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ String close, open;
+ Map attrs = null;
+
+ if (reader.matches(parser.getOpenScriptlet()))
+ {
+ open = parser.getOpenScriptlet();
+ close = parser.getCloseScriptlet();
+ }
+ else
+ {
+ return false;
+ }
+
+ reader.advance(open.length());
+
+ JETMark start = reader.mark();
+ JETMark stop = reader.skipUntil(close);
+ if (stop == null)
+ {
+ throw new JETException(MessagesUtil.getUnterminatedMessage(reader, close, open));
+ }
+ listener.handleScriptlet(start, stop, attrs);
+ return true;
+ }
+
+ }
+
+ public static class Expression implements JETCoreElement
+ {
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ String close, open;
+ Map attrs = null;
+
+ if (reader.matches(parser.getOpenExpr()))
+ {
+ open = parser.getOpenExpr();
+ close = parser.getCloseExpr();
+ }
+ else
+ {
+ return false;
+ }
+
+ reader.advance(open.length());
+
+ JETMark start = reader.mark();
+ JETMark stop = reader.skipUntil(close);
+ if (stop == null)
+ {
+ throw new JETException(MessagesUtil.getUnterminatedMessage(reader, open, close));
+ }
+ listener.handleExpression(start, stop, attrs);
+ return true;
+ }
+ }
+
+ /**
+ * Quoting in template text.
+ * Entities &apos; and &quote;
+ */
+ public static class QuoteEscape implements JETCoreElement
+ {
+ /**
+ * constants for escapes
+ */
+ protected static final String APOS = "&apos;"; //$NON-NLS-1$
+
+ protected static final String QUOTE = "&quote;"; //$NON-NLS-1$
+
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ try
+ {
+ if (reader.matches(parser.getQuotedEndTag()))
+ {
+ reader.advance(parser.getQuotedEndTag().length());
+ parser.writer.write(parser.getEndTag());
+ parser.flushCharData();
+ return true;
+ }
+ else if (reader.matches(APOS))
+ {
+ reader.advance(APOS.length());
+ parser.writer.write("\'"); //$NON-NLS-1$
+ parser.flushCharData();
+ return true;
+ }
+ else if (reader.matches(QUOTE))
+ {
+ reader.advance(QUOTE.length());
+ parser.writer.write("\""); //$NON-NLS-1$
+ parser.flushCharData();
+ return true;
+ }
+ }
+ catch (IOException exception)
+ {
+ throw new JETException(exception);
+ }
+ return false;
+ }
+ }
+
+ public static class Directive implements JETCoreElement
+ {
+ protected Collection directives = new ArrayList();
+
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ if (reader.matches(parser.getOpenDirective()))
+ {
+ JETMark start = reader.mark();
+ reader.advance(parser.getOpenDirective().length());
+ reader.skipSpaces();
+
+ // Check which directive it is.
+ //
+ String match = null;
+
+ for (Iterator i = directives.iterator(); i.hasNext();)
+ {
+ String directive = (String)i.next();
+ if (reader.matches(directive))
+ {
+ match = directive;
+ break;
+ }
+ }
+
+ if (match == null)
+ {
+ throw new JETException(MessagesUtil.getString(
+ "jet.error.bad.directive", //$NON-NLS-1$
+ new Object []{ start.format("jet.mark.file.line.column") })); //$NON-NLS-1$
+ //reader.reset(start);
+ //return false;
+ }
+
+ reader.advance(match.length());
+
+ // Parse the attr-val pairs.
+ //
+ Map attrs = reader.parseTagAttributes();
+
+ // Match close.
+ reader.skipSpaces();
+ if (!reader.matches(parser.getCloseDirective()))
+ {
+ throw new JETException(MessagesUtil.getUnterminatedMessage(reader, parser.getOpenDirective(),
+ parser.getCloseDirective()));
+ }
+ else
+ {
+ reader.advance(parser.getCloseDirective().length());
+ }
+
+ JETMark stop = reader.mark();
+ listener.handleDirective(match, start, stop, attrs);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public Collection getDirectives()
+ {
+ return directives;
+ }
+ }
+
+ protected void flushCharData() throws JETException
+ {
+ char[] array = writer.toCharArray();
+ if (array.length != 0) // Avoid unnecessary out.write("") statements...
+ {
+ writer = new CharArrayWriter();
+ listener.handleCharData(array);
+ }
+ }
+
+ public void parse() throws JETException
+ {
+ parse(null);
+ }
+
+ public void parse(String until) throws JETException
+ {
+ parse(until, null);
+ }
+
+ public void parse(String until, Class[] accept) throws JETException
+ {
+ while (reader.hasMoreInput())
+ {
+ if (until != null && reader.matches(until))
+ {
+ return;
+ }
+
+ Iterator e = coreElements.iterator();
+
+ if (accept != null)
+ {
+ List v = new ArrayList();
+ while (e.hasNext())
+ {
+ JETCoreElement c = (JETCoreElement)e.next();
+ for (int i = 0; i < accept.length; i++)
+ {
+ if (c.getClass().equals(accept[i]))
+ {
+ v.add(c);
+ }
+ }
+ }
+ e = v.iterator();
+ }
+
+ boolean accepted = false;
+ while (e.hasNext())
+ {
+ JETCoreElement c = (JETCoreElement)e.next();
+ reader.mark();
+ if (c.accept(listener, reader, this))
+ {
+ accepted = true;
+ break;
+ }
+ }
+ if (!accepted)
+ {
+ String s = reader.nextContent();
+ writer.write(s, 0, s.length());
+ }
+ }
+ flushCharData();
+ }
+
+ public void stackPopped() {
+ try {
+ listener.endPageProcessing();
+ } catch (JETException e) {
+ if(listener instanceof JETParseEventListener2)
+ ((JETParseEventListener2)listener).recordProblem(ProblemSeverity.ERROR, IProblem.JETException, e.getLocalizedMessage(), new Object[0], -1, -1, -1, -1);
+ }
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETReader.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETReader.java
new file mode 100644
index 0000000..9adc8ae
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/JETReader.java
@@ -0,0 +1,832 @@
+/**
+ * <copyright>
+ *
+ * Copyright (c) 2002, 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JETReader.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+import java.io.BufferedReader;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.eclipse.jet.internal.core.parser.LineInfo;
+
+
+/**
+ * JETReader is an input buffer for the JSP parser. It should allow
+ * unlimited lookahead and pushback. It also has a bunch of parsing
+ * utility methods for understanding htmlesque thingies.
+ */
+public class JETReader
+{
+ public interface IStackPopNotifier {
+ public abstract void stackPopped();
+ }
+
+ private static final char XML_TAG_START = '<';
+
+ protected char startTagInitialChar = '<';
+
+ protected char endTagInitialChar = '%';
+
+ protected char endTagFinalChar = '>';
+
+ protected JETMark current = null;
+
+ protected String master = null;
+
+ protected List sourceFiles = new ArrayList();
+
+ protected List baseURIs = new ArrayList();
+
+ protected int size = 0;
+
+ protected boolean trimExtraNewLine = true;
+
+ private IStackPopNotifier stackPopNotifier = null;
+
+ public JETReader(String locationURI, Reader reader) throws JETException {
+ this(null, locationURI, reader);
+ }
+
+ public JETReader(String baseURI, String locationURI, Reader reader) throws JETException {
+ stackStream(baseURI, locationURI, reader);
+ }
+
+ /**
+ *
+ * @param baseURI
+ * @param locationURI
+ * @param inputStream
+ * @param encoding
+ * @throws JETException
+ * @deprecated Use {@link #JETReader(String, String, Reader)}.
+ */
+ public JETReader(String baseURI, String locationURI, InputStream inputStream, String encoding) throws JETException
+ {
+ stackStream(baseURI, locationURI, inputStream, encoding);
+ }
+
+ /**
+ *
+ * @param locationURI
+ * @param inputStream
+ * @param encoding
+ * @throws JETException
+ * @deprecated Use {@link #JETReader(String, Reader)}.
+ */
+ public JETReader(String locationURI, InputStream inputStream, String encoding) throws JETException
+ {
+ this(null, locationURI, inputStream, encoding);
+ }
+
+ public String getFile(int fileid)
+ {
+ return (String)sourceFiles.get(fileid);
+ }
+
+ public String getBaseURI(int fileid)
+ {
+ return (String)baseURIs.get(fileid);
+ }
+
+ public void stackStream(String locationURI, Reader reader) throws JETException {
+ stackStream(null, locationURI, reader);
+ }
+
+ public void stackStream(String baseURI, String locationURI, Reader reader) throws JETException {
+ try {
+ // Register the file, and read its content:
+ //
+ int fileid = registerSourceFile(locationURI);
+ registerBaseURI(baseURI);
+ final char[] charArray = getContents(reader);
+ if (current == null) {
+ current = new JETMark(this, charArray, fileid, locationURI);
+ } else {
+ current.pushStream(charArray, fileid, locationURI);
+ }
+ } catch (UnsupportedEncodingException exception) {
+ throw new JETException(exception);
+ } catch (IOException exception) {
+ throw new JETException(exception);
+ }
+
+ }
+ /**
+ *
+ * @param locationURI
+ * @param iStream
+ * @param encoding
+ * @throws JETException
+ * @deprecated Use {@link #stackStream(String, Reader)}.
+ */
+ public void stackStream(String locationURI, InputStream iStream, String encoding) throws JETException
+ {
+ try {
+ stackStream(locationURI, new BufferedReader(new InputStreamReader(iStream, encoding)));
+ } catch (UnsupportedEncodingException e) {
+ throw new JETException(e);
+ }
+ }
+
+ /**
+ * Stack a stream for parsing
+ * @param iStream Stream ready to parse
+ * @param encoding Optional encoding to read the file.
+ * @deprecated Use {@link #stackStream(String, String, Reader)}.
+ */
+ public void stackStream(String baseURI, String locationURI,
+ InputStream iStream, String encoding) throws JETException {
+ try {
+ stackStream(baseURI, locationURI, new BufferedReader(new InputStreamReader(iStream, encoding)));
+ } catch (UnsupportedEncodingException e) {
+ throw new JETException(e);
+ }
+ }
+
+ /**
+ * Read the contents of the reader into a char array.
+ * @param reader the reader.
+ * @return the contents as a char array.
+ * @throws IOException if an IO error occurs
+ */
+ private char[] getContents(Reader reader) throws IOException {
+ try {
+ CharArrayWriter writer = new CharArrayWriter();
+ char buf[] = new char [1024];
+ for (int i = 0; (i = reader.read(buf)) != -1;){
+ // Remove zero width non-breaking space, which may be used as a byte
+ // order marker,
+ // and may be ignored according to the Unicode FAQ:
+ // http://www.unicode.org/unicode/faq/utf_bom.html#38
+ //
+ if (buf[0] == '\uFEFF') {
+ writer.write(buf, 1, i - 1);
+ }
+ else {
+ writer.write(buf, 0, i);
+ }
+ }
+ writer.close();
+ final char[] charArray = writer.toCharArray();
+ return charArray;
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (Exception exception) {
+ // nothing we can do, suppress it.
+ }
+ }
+ }
+
+ }
+
+ public boolean popFile()
+ {
+ // Is stack created ? (will happen if the JET file we're looking at is missing.
+ //
+ if (current == null)
+ {
+ return false;
+ }
+
+ // Restore parser state:
+ //
+ size--;
+
+ if(stackPopNotifier != null) {
+ stackPopNotifier.stackPopped();
+ }
+
+ return current.popStream();
+ }
+
+ /**
+ * Register a new source file.
+ * This method is used to implement file inclusion. Each included file
+ * gets a uniq identifier (which is the index in the array of source files).
+ * @return The index of the now registered file.
+ */
+ protected int registerSourceFile(String file)
+ {
+ sourceFiles.add(file);
+ ++this.size;
+ return sourceFiles.size() - 1;
+ }
+
+ /**
+ * Register a new baseURI.
+ * This method is used to implement file inclusion. Each included file
+ * gets a uniq identifier (which is the index in the array of base URIs).
+ * @return The index of the now registered file.
+ */
+ protected void registerBaseURI(String baseURI)
+ {
+ baseURIs.add(baseURI);
+ }
+
+ /**
+ * Returns whether more input is available. If the end of the buffer for an included file is reached, it will return
+ * to the context of the previous file, and return whether more input is available from there. In this case, if
+ * trimExtraNewLine is true, then an unwanted extra newline character will be suppressed. We consider the first
+ * newline in the buffer we are returning to be unwanted if the ending buffer already has at least one trailing
+ * newline.
+ */
+ public boolean hasMoreInput()
+ {
+ if (current.cursor < current.stream.length)
+ {
+ return true;
+ }
+
+ boolean nl = hasTrailingNewLine();
+ while (popFile())
+ {
+ if (current.cursor < current.stream.length)
+ {
+ if (trimExtraNewLine && nl)
+ {
+ skipNewLine();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether the current stream has at least one trailing newline, optionally followed by spaces.
+ */
+ protected boolean hasTrailingNewLine()
+ {
+ if(current.lineInfo != null && current.lineInfo.length > 0)
+ {
+ return current.lineInfo[current.lineInfo.length - 1].getDelimiter().length() > 0;
+ } else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * If the next character would be a line break, moves the cursor past it.
+ */
+ protected void skipNewLine()
+ {
+ LineInfo li = LineInfo.getLineInfo(current.lineInfo, current.cursor);
+ if(current.cursor >= li.getEnd()) {
+ current.cursor = li.getEnd() + li.getDelimiter().length();
+ }
+ }
+
+ public int nextChar()
+ {
+ if (!hasMoreInput())
+ {
+ return -1;
+ }
+
+ int ch = current.stream[current.cursor];
+
+ ++current.cursor;
+
+ return ch;
+ }
+
+ /**
+ * Gets Content until the next potential JSP element. Because all elements
+ * begin with a '&lt;' we can just move until we see the next one.
+ */
+ public String nextContent()
+ {
+ int cur_cursor = current.cursor;
+ int len = current.stream.length;
+ if (cur_cursor == len)
+ return ""; //$NON-NLS-1$
+ // pure obsfuscated genius!
+ while (++current.cursor < len && (current.stream[current.cursor]) != startTagInitialChar && current.stream[current.cursor] != XML_TAG_START)
+ {
+ // do nothing
+ }
+
+ return new String(current.stream, cur_cursor, current.cursor - cur_cursor);
+ }
+
+ public char[] getChars(JETMark start, JETMark stop)
+ {
+ JETMark oldstart = mark();
+ reset(start);
+ CharArrayWriter writer = new CharArrayWriter();
+ while (!stop.equals(mark()))
+ {
+ writer.write(nextChar());
+ }
+ writer.close();
+ reset(oldstart);
+ return writer.toCharArray();
+ }
+
+ public int peekChar()
+ {
+ if (hasMoreInput())
+ {
+ return current.stream[current.cursor];
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ public JETMark mark()
+ {
+ return new JETMark(current);
+ }
+
+ public void reset(JETMark mark)
+ {
+ current = new JETMark(mark);
+ }
+
+ public boolean matchesIgnoreCase(String string)
+ {
+ JETMark mark = mark();
+ int ch = 0;
+ int i = 0;
+ do
+ {
+ ch = nextChar();
+ if (Character.toLowerCase((char)ch) != string.charAt(i++))
+ {
+ reset(mark);
+ return false;
+ }
+ }
+ while (i < string.length());
+ reset(mark);
+ return true;
+ }
+
+ public boolean matches(String string)
+ {
+ JETMark mark = mark();
+ int ch = 0;
+ int i = 0;
+ do
+ {
+ ch = nextChar();
+ if (((char)ch) != string.charAt(i++))
+ {
+ reset(mark);
+ return false;
+ }
+ }
+ while (i < string.length());
+ reset(mark);
+ return true;
+ }
+
+ public void advance(int n)
+ {
+ while (--n >= 0)
+ nextChar();
+ }
+
+ public int skipSpaces()
+ {
+ int i = 0;
+ while (isSpace())
+ {
+ ++i;
+ nextChar();
+ }
+ return i;
+ }
+
+ /**
+ * Skip until the given string is matched in the stream.
+ * When returned, the context is positioned past the end of the match.
+ * @param limit The String to match.
+ * @return A non-null <code>JETMark</code> instance if found,
+ * <strong>null</strong> otherwise.
+ */
+ public JETMark skipUntil(String limit)
+ {
+ JETMark ret = null;
+ int limlen = limit.length();
+ int ch;
+
+ skip: for (ret = mark(), ch = nextChar(); ch != -1; ret = mark(), ch = nextChar())
+ {
+ if (ch == limit.charAt(0))
+ {
+ for (int i = 1; i < limlen; i++)
+ {
+ if (Character.toLowerCase((char)nextChar()) != limit.charAt(i))
+ {
+ continue skip;
+ }
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ protected boolean isSpace()
+ {
+ int c = peekChar();
+ return c <= ' ' && c >= 0;
+ }
+
+ /**
+ * Parse a space delimited token.
+ * If quoted the token will consume all characters up to a matching quote,
+ * otherwise, it consumes up to the first delimiter character.
+ * @param quoted If <strong>true</strong> accept quoted strings.
+ */
+ public String parseToken(boolean quoted) throws JETException
+ {
+ return parseToken(quoted, true /* skip spaces*/);
+ }
+
+ /**
+ * Parse an attribute/value pair, and store it in provided hash table.
+ * The attribute/value pair is defined by:
+ * <pre>
+ * av := spaces token spaces '=' spaces token spaces
+ * </pre>
+ * Where <em>token</em> is defined by <code>parseToken</code> and
+ * <em>spaces</em> is defined by <code>skipSpaces</code>.
+ * The name is always considered case insensitive, hence stored in its
+ * lower case version.
+ * @param into The HashMap instance to save the result to.
+ */
+ protected void parseAttributeValue(HashMap into) throws JETException
+ {
+ // Get the attribute name:
+ //
+ skipSpaces();
+ String name = parseToken(false);
+
+ // Check for an equal sign:
+ //
+ skipSpaces();
+ if (peekChar() != '=')
+ {
+ throw new JETException(MessagesUtil.getString("jet.error.attr.novalue", new Object []{ name, mark().toString() })); //$NON-NLS-1$
+ }
+ nextChar();
+
+ // Get the attribute value:
+ //
+ skipSpaces();
+ String value = parseToken(true);
+ skipSpaces();
+
+ // Add the binding to the provided hashtable:
+ //
+ into.put(name, value);
+ }
+
+ /**
+ * Parse some tag attributes for Beans.
+ * The stream is assumed to be positioned right after the tag name. The
+ * syntax recognized is:
+ * <pre>
+ * tag-attrs := empty | attr-list ("&gt;" | "--&gt;" | %&gt;)
+ * attr-list := empty | av spaces attr-list
+ * empty := spaces
+ * </pre>
+ * Where <em>av</em> is defined by <code>parseAttributeValue</code>.
+ * @return A HashMap mapping String instances (variable names) into
+ * String instances (variable values).
+ */
+ public HashMap parseTagAttributesBean() throws JETException
+ {
+ HashMap values = new HashMap(11);
+ while (true)
+ {
+ skipSpaces();
+ int ch = peekChar();
+ if (ch == endTagFinalChar)
+ {
+ // End of the useBean tag.
+ //
+ return values;
+ }
+ else if (ch == '/')
+ {
+ JETMark mark = mark();
+ nextChar();
+
+ // XMLesque Close tags
+ //
+ try
+ {
+ if (nextChar() == endTagFinalChar)
+ {
+ return values;
+ }
+ }
+ finally
+ {
+ reset(mark);
+ }
+ }
+ if (ch == -1)
+ {
+ break;
+ }
+
+ // Parse as an attribute=value:
+ //
+ parseAttributeValue(values);
+ }
+
+ // Reached EOF:
+ //
+ throw new JETException(MessagesUtil.getString("jet.error.tag.attr.unterminated", new Object []{ mark().toString() })); //$NON-NLS-1$
+ }
+
+ public JETMark[] parseXmlAttribute() throws JETException
+ {
+ JETMark[] attributeMarks = new JETMark [5];
+
+ attributeMarks[0] = mark(); // start of name
+ String name = parseToken(false, false);
+ attributeMarks[1] = mark(); // end of name
+
+ skipSpaces();
+ if (peekChar() != '=')
+ {
+ throw new JETException(MessagesUtil.getString("jet.error.param.novalue", new Object []{ name, mark().toString() })); //$NON-NLS-1$
+ }
+ attributeMarks[2] = mark(); // equals
+ nextChar();
+
+ // Get the attribute value:
+ skipSpaces();
+ attributeMarks[3] = mark(); // value start
+ parseToken(true /* quoted token */, false);
+ attributeMarks[4] = mark();
+
+ return attributeMarks;
+ }
+
+ /**
+ * Parse some tag attributes. The stream is assumed to be positioned right
+ * after the tag name. The syntax recognized is:
+ *
+ * <pre>
+ * tag-attrs := empty | attr-list (&quot;&gt;&quot; | &quot;--&gt;&quot; | %&gt;)
+ * attr-list := empty | av spaces attr-list
+ * empty := spaces
+ * </pre>
+ *
+ * Where <em>av</em> is defined by <code>parseAttributeValue</code>.
+ *
+ * @return A HashMap mapping String instances (variable names) into String
+ * instances (variable values).
+ */
+ public HashMap parseTagAttributes() throws JETException
+ {
+ HashMap values = new LinkedHashMap(11);
+ while (true)
+ {
+ skipSpaces();
+ int ch = peekChar();
+ if (ch == endTagFinalChar)
+ {
+ return values;
+ }
+
+ if (ch == '-')
+ {
+ JETMark mark = mark();
+ nextChar();
+ // Close NCSA like attributes "->"
+ try
+ {
+ if (nextChar() == '-' && nextChar() == endTagFinalChar)
+ {
+ return values;
+ }
+ }
+ finally
+ {
+ reset(mark);
+ }
+ }
+ else if (ch == endTagInitialChar)
+ {
+ JETMark mark = mark();
+ nextChar();
+ // Close variable like attributes "%>"
+ try
+ {
+ if (nextChar() == endTagFinalChar)
+ {
+ return values;
+ }
+ }
+ finally
+ {
+ reset(mark);
+ }
+ }
+ else if (ch == '/')
+ {
+ JETMark mark = mark();
+ nextChar();
+ // XMLesque Close tags
+ try
+ {
+ if (nextChar() == endTagFinalChar)
+ {
+ return values;
+ }
+ }
+ finally
+ {
+ reset(mark);
+ }
+ }
+ if (ch == -1)
+ {
+ return values;
+ }
+ // Parse as an attribute=value:
+ parseAttributeValue(values);
+ }
+ // Reached EOF:
+ // throw new JETException(CodeGenPlugin.getPlugin().getString("jet.error.tag.attr.unterminated", new Object [] { mark().toString() }));
+ }
+
+ /**
+ * Parse utils - Is current character a token delimiter ?
+ * Delimiters are currently defined to be =, &gt;, &lt;, ", and ' or any
+ * any space character as defined by <code>isSpace</code>.
+ * @return A boolean.
+ */
+ protected boolean isDelimiter()
+ {
+ if (!isSpace())
+ {
+ int ch = peekChar();
+
+ // Look for a single-char work delimiter:
+ //
+ if (ch == '=' || ch == endTagFinalChar || ch == '"' || ch == '\'' || ch == '/' || ch == '>')
+ {
+ return true;
+ }
+
+ // Look for an end-of-comment or end-of-tag:
+ //
+ if (ch == '-')
+ {
+ JETMark mark = mark();
+ if (((ch = nextChar()) == endTagFinalChar) || ((ch == '-') && (nextChar() == endTagFinalChar)))
+ {
+ reset(mark);
+ return true;
+ }
+ else
+ {
+ reset(mark);
+ return false;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ public void setStartTag(String startTag)
+ {
+ startTagInitialChar = startTag.charAt(0);
+ }
+
+ public void setEndTag(String endTag)
+ {
+ endTagFinalChar = endTag.charAt(endTag.length() - 1);
+ endTagInitialChar = endTag.charAt(0);
+ }
+
+ /**
+ * Parse a space delimited token.
+ * If quoted the token will consume all characters up to a matching quote,
+ * otherwise, it consumes up to the first delimiter character.
+ * @param quoted If <strong>true</strong> accept quoted strings.
+ * @param skipSpaces if <code>true</code>, skip leading spaces to find the token, false otherwise.
+ * @return the token, or an empty string if no token is found.
+ */
+ public String parseToken(boolean quoted, boolean skipSpaces) throws JETException
+ {
+ StringBuffer stringBuffer = new StringBuffer();
+ if (skipSpaces)
+ {
+ skipSpaces();
+ }
+ stringBuffer.setLength(0);
+
+ int ch = peekChar();
+
+ if (quoted)
+ {
+ if (ch == '"' || ch == '\'')
+ {
+ char endQuote = ch == '"' ? '"' : '\'';
+
+ // Consume the open quote:
+ //
+ ch = nextChar();
+ for (ch = nextChar(); ch != -1 && ch != endQuote; ch = nextChar())
+ {
+ if (ch == '\\')
+ {
+ ch = nextChar();
+ }
+ stringBuffer.append((char)ch);
+ }
+
+ // Check end of quote, skip closing quote:
+ //
+ if (ch == -1)
+ {
+ throw new JETException(MessagesUtil.getString("jet.error.quotes.unterminated", new Object []{ mark().toString() })); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ throw new JETException(MessagesUtil.getString("jet.error.attr.quoted", new Object []{ mark().toString() })); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ if (!isDelimiter())
+ {
+ // Read value until delimiter is found:
+ do
+ {
+ ch = nextChar();
+ // Take care of the quoting here.
+ if (ch == '\\')
+ {
+ if (peekChar() == '"' || peekChar() == '\'' || peekChar() == endTagFinalChar || peekChar() == endTagInitialChar || peekChar() == '>')
+ {
+ ch = nextChar();
+ }
+ }
+ else if (ch == -1)
+ {
+ break;
+ }
+ stringBuffer.append((char)ch);
+ }
+ while (!isDelimiter());
+ }
+ }
+ return stringBuffer.toString();
+ }
+
+ public char[] getChars()
+ {
+ return current.stream;
+ }
+
+
+ /**
+ * Set the stack pop notifier
+ * @param stackPopNotifier an instance of {@link IStackPopNotifier} or <code>null</code>
+ */
+ public void setStackPopNotifier(IStackPopNotifier stackPopNotifier) {
+ this.stackPopNotifier = stackPopNotifier;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/Messages.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/Messages.java
new file mode 100644
index 0000000..3f18413
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/Messages.java
@@ -0,0 +1,22 @@
+package org.eclipse.jet.internal.core.parser.jasper;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class Messages {
+ private static final String BUNDLE_NAME = "org.eclipse.jet.internal.tools.parser.jasper.messages"; //$NON-NLS-1$
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ private Messages() {
+ }
+
+ public static String getString(String key) {
+ try {
+ return RESOURCE_BUNDLE.getString(key);
+ } catch (MissingResourceException e) {
+ return '!' + key + '!';
+ }
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/MessagesUtil.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/MessagesUtil.java
new file mode 100644
index 0000000..c18c09d
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/MessagesUtil.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 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.jet.internal.core.parser.jasper;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+
+/**
+ * Utility class for formatting messages.
+ *
+ */
+public class MessagesUtil {
+
+ private MessagesUtil() {
+ // do nothing
+ }
+
+ public static String getString(String key, Object[] args) {
+ return MessageFormat.format(Messages.getString(key), args);
+ }
+
+ /**
+ * @param reader
+ * @param close
+ * @param open
+ */
+ public static String getUnterminatedMessage(JETReader reader, String close, String open) {
+ final String msg = MessagesUtil.getString(
+ "jet.error.unterminated", //$NON-NLS-1$
+ new Object[] { open, reader.mark().toString() });
+ return msg;
+ }
+
+ public static void recordUnterminatedElement(
+ JETParseEventListener2 listener,
+ String expectedCloseSequence,
+ JETMark elementStart,
+ JETReader reader)
+ {
+ listener.recordProblem(
+ ProblemSeverity.ERROR,
+ IProblem.UnterminatedXMLTag,
+ Messages.getString("jet.error.unterminated"), //$NON-NLS-1$
+ new Object []{ expectedCloseSequence },
+ elementStart.getCursor(),
+ reader.mark().getCursor(),
+ elementStart.getLine(),
+ elementStart.getCol());
+
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/XMLElementDelegate.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/XMLElementDelegate.java
new file mode 100644
index 0000000..214cc29
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/XMLElementDelegate.java
@@ -0,0 +1,168 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: XMLElementDelegate.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.core.parser.jasper;
+
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jet.core.parser.IProblem;
+import org.eclipse.jet.core.parser.ProblemSeverity;
+
+
+/**
+ * A JET Parser delegate that handles XML Elements.
+ */
+public class XMLElementDelegate implements JETCoreElement
+{
+
+ private static final String XML_TAG_CLOSE = ">"; //$NON-NLS-1$
+
+ private static final String SLASH = "/"; //$NON-NLS-1$
+
+ private static final String XML_CLOSE_TAG_OPEN = "</"; //$NON-NLS-1$
+
+ private static final String XML_TAG_OPEN = "<"; //$NON-NLS-1$
+
+ public XMLElementDelegate()
+ {
+ super();
+ }
+
+ public boolean accept(JETParseEventListener listener, JETReader reader, JETParser parser) throws JETException
+ {
+ if (!(listener instanceof JETParseEventListener2))
+ {
+ return false;
+ }
+ JETParseEventListener2 jet2Listener = (JETParseEventListener2)listener;
+
+ String elementOpen = XML_TAG_OPEN;
+ if (!reader.matches(elementOpen))
+ {
+ return false;
+ }
+ JETMark start = reader.mark();
+ reader.advance(elementOpen.length());
+
+ boolean isEndTag = false;
+ boolean isEmptyTag = false;
+ if (reader.matches(SLASH))
+ {
+ isEndTag = true;
+ reader.advance(1);
+ elementOpen = XML_CLOSE_TAG_OPEN;
+ }
+
+ String tagName = reader.parseToken(false, false /*don't skip spaces*/);
+
+ boolean knownInvalidTagName = jet2Listener.isKnownInvalidTagName(tagName);
+ if (!jet2Listener.isKnownTag(tagName) && !knownInvalidTagName)
+ {
+ reader.reset(start);
+ return false;
+ }
+
+ JETMark interiorStartMark = reader.mark();
+ // check for the end position...
+ JETMark endTagMark = reader.skipUntil(XML_TAG_CLOSE);
+ if (endTagMark == null)
+ {
+ MessagesUtil.recordUnterminatedElement(jet2Listener, XML_TAG_CLOSE, start, reader);
+ return true;
+ }
+
+ // now reset to the tag interior for detailed parsing...
+ reader.reset(interiorStartMark);
+
+ Map attributeMap = Collections.EMPTY_MAP;
+ if (isEndTag)
+ {
+ reader.skipSpaces();
+ }
+ else
+ {
+ // use Linked hash map to keep attributes in order of declaration
+ attributeMap = new LinkedHashMap();
+ reader.skipSpaces();
+ while (reader.peekChar() != '>' && reader.peekChar() != '/')
+ {
+ JETMark[] attrMarks = reader.parseXmlAttribute();
+ String name = new String(reader.getChars(attrMarks[0], attrMarks[1]));
+ String value = new String(reader.getChars(attrMarks[3], attrMarks[4]));
+ if (attributeMap.containsKey(name))
+ {
+ String msg = Messages.getString("XMLElementDelegate_DuplicateAttribute"); //$NON-NLS-1$
+ jet2Listener.recordProblem(
+ ProblemSeverity.ERROR,
+ IProblem.DuplicateAttribute,
+ msg,
+ null,
+ attrMarks[0].getCursor(),
+ attrMarks[4].getCursor(),
+ attrMarks[0].getLine(), attrMarks[0].getCol());
+ }
+ else
+ {
+ attributeMap.put(name, value.substring(1, value.length() - 1));
+ }
+ reader.skipSpaces();
+ }
+ // attributeMap = reader.parseTagAttributes();
+ }
+ // find the closing char...
+ if (reader.peekChar() == '/' && !isEndTag)
+ {
+ reader.nextChar();
+ isEmptyTag = true;
+ }
+ if (reader.peekChar() != '>')
+ {
+ MessagesUtil.recordUnterminatedElement(jet2Listener, ">", start, reader); //$NON-NLS-1$
+ }
+ reader.nextChar();
+ JETMark stop = reader.mark();
+ if(knownInvalidTagName)
+ {
+ jet2Listener.recordProblem(
+ ProblemSeverity.WARNING,
+ IProblem.UnknownXMLTag,
+ Messages.getString("XMLElementDelegate_UnknownXMLTag"), //$NON-NLS-1$
+ new Object[] {tagName},
+ start.getCursor(),
+ stop.getCursor(),
+ start.getLine(), start.getCol());
+ reader.reset(start);
+ return false;
+ }
+ else if (isEmptyTag)
+ {
+ jet2Listener.handleXMLEmptyTag(tagName, start, stop, attributeMap);
+ }
+ else if (isEndTag)
+ {
+ jet2Listener.handleXMLEndTag(tagName, start, stop);
+ }
+ else
+ {
+ jet2Listener.handleXMLStartTag(tagName, start, stop, attributeMap);
+ }
+ return true;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/messages.properties b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/messages.properties
new file mode 100644
index 0000000..10926e2
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/core/parser/jasper/messages.properties
@@ -0,0 +1,21 @@
+jet.mark.file.line.column = ''{0}'' at line {1} column {2}
+jet.mark.file.line = ''{0}'' at line {1}
+
+jet.error.bad.attribute = Bad attribute named ''{0}'' in {1}
+jet.error.missing.attribute = Missing attribute named ''{0}'' in {1}
+jet.error.bad.directive = Bad directive in {0}
+
+jet.error.attr.novalue = {0} {1}
+jet.error.attr.quoted = Quoted attribute expected in {1}
+jet.error.file.cannot.read = The file ''{0}'' cannot be read in {1}
+jet.error.param.novalue = No value for ''{0}'' in {1}
+jet.error.quotes.unterminated = Unterminate quote in {0}
+jet.error.tag.attr.unterminated = Unterminated tag attribute in {0}
+jet.error.unterminated = Unterminated ''{0}'' in {1}
+jet.error.missing.jet.directive = The ''jet'' directive is missing in {0}
+
+jet.error.unmatched.directive = No matching ''{0}'' directive for ''{1}'' directive in {2}
+jet.error.section.noinclude = No preceding alternative ''include'' directive for section in {0}
+
+XMLElementDelegate_DuplicateAttribute=Duplicate attribute definition
+XMLElementDelegate_UnknownXMLTag=Unknown tag ''{0}''
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.java
new file mode 100644
index 0000000..c9652f2
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.java
@@ -0,0 +1,100 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2Messages.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.l10n;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Access class for string resources.
+ */
+public class JET2Messages {
+ private static final String BUNDLE_NAME = "org.eclipse.jet.internal.l10n.JET2Messages"; //$NON-NLS-1$
+
+ private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
+ .getBundle(BUNDLE_NAME);
+
+ public static String getString(String key) {
+ try {
+ return RESOURCE_BUNDLE.getString(key);
+ } catch (MissingResourceException e) {
+ return '!' + key + '!';
+ }
+ }
+
+ private JET2Messages() {
+ }
+
+ public static String JET2Compiler_DeprecatedAttribute = getString("JET2Compiler_DeprecatedAttribute"); //$NON-NLS-1$
+
+ public static String JET2Compiler_DeprecatedTag = getString("JET2Compiler_DeprecatedTag"); //$NON-NLS-1$
+
+ public static String JET2Compiler_ErrorCreatingMarkers = getString("JET2Compiler_ErrorCreatingMarkers"); //$NON-NLS-1$
+
+ public static String JET2Compiler_ErrorRemovingMarkers = getString("JET2Compiler_ErrorRemovingMarkers"); //$NON-NLS-1$
+
+ public static String JET2Compiler_ErrorWritingJava = getString("JET2Compiler_ErrorWritingJava"); //$NON-NLS-1$
+
+ public static String JET2Compiler_MissingDirectiveAttribute = getString("JET2Compiler_MissingDirectiveAttribute"); //$NON-NLS-1$
+
+ public static String JET2Compiler_MissingEndTag = getString("JET2Compiler_MissingEndTag"); //$NON-NLS-1$
+
+ public static final String JET2Compiler_MissingFile = getString("JET2Compiler_MissingFile"); //$NON-NLS-1$;
+
+ public static final String JET2Compiler_StartDirectiveOutOfContext = getString("JET2Compiler_StartDirectiveOutOfContext"); //$NON-NLS-1$
+
+ public static final String JET2Compiler_EndDirectiveOutOfContext = getString("JET2Compiler_EndDirectiveOutOfContext"); //$NON-NLS-1$
+
+ public static String JET2Compiler_MissingRequiredAttribute = getString("JET2Compiler_MissingRequiredAttribute"); //$NON-NLS-1$
+
+ public static String JET2Compiler_MissingStartTag = getString("JET2Compiler_MissingStartTag"); //$NON-NLS-1$
+
+ public static String JET2Compiler_PrefixAlreadyAssigned = getString("JET2Compiler_PrefixAlreadyAssigned"); //$NON-NLS-1$
+
+ public static String JET2Compiler_SameJavaClassAsOther = getString("JET2Compiler_SameJavaClassAsOther"); //$NON-NLS-1$
+
+ public static String JET2Compiler_TagCannotHaveContent = getString("JET2Compiler_TagCannotHaveContent"); //$NON-NLS-1$
+
+ public static String JET2Compiler_TagRequiresContent = getString("JET2Compiler_TagRequiresContent"); //$NON-NLS-1$
+
+ public static String JET2Compiler_TagShouldBeEmptyFormat = getString("JET2Compiler_TagShouldBeEmptyFormat"); //$NON-NLS-1$
+
+ public static String JET2Compiler_UnknownAttribute = getString("JET2Compiler_UnknownAttribute"); //$NON-NLS-1$
+
+ public static String JET2Compiler_UnknownTagLibrary = getString("JET2Compiler_UnknownTagLibrary"); //$NON-NLS-1$
+
+ public static String ASTCompilerParseListener_UnsupportedDirective = getString("ASTCompilerParseListener_UnsupportedDirective"); //$NON-NLS-1$
+
+ public static String JET2Context_ExecutionCancelled = getString("JET2Context_ExecutionCancelled"); //$NON-NLS-1$
+
+ public static String JET2Context_InvalidVariableName = getString("JET2Context_InvalidVariableName"); //$NON-NLS-1$
+
+ public static String JET2Context_SuccessfulExecution = getString("JET2Context_SuccessfulExecution"); //$NON-NLS-1$
+
+ public static String JET2Context_SuccessfulWithMessages = getString("JET2Context_SuccessfulWithMessages"); //$NON-NLS-1$
+
+ public static String JET2Context_SuccessfulWithWarnings = getString("JET2Context_SuccessfulWithWarnings"); //$NON-NLS-1$
+
+ public static String JET2Context_ErrorsInExecution = getString("JET2Context_ErrorsInExecution"); //$NON-NLS-1$
+
+ public static String JET2Context_CouldNotFindTemplate = getString("JET2Context_CouldNotFindTemplate"); //$NON-NLS-1$
+
+ public static String JET2Context_CommittingActions = getString("JET2Context_CommittingActions"); //$NON-NLS-1$
+
+ public static String JET2Context_VariableNotFound = getString("JET2Context_VariableNotFound"); //$NON-NLS-1$
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.properties b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.properties
new file mode 100644
index 0000000..f77e1e3
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/l10n/JET2Messages.properties
@@ -0,0 +1,48 @@
+#
+# <copyright>
+#
+# Copyright (c) 2006, 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 - Initial API and implementation
+#
+# </copyright>
+#
+# $Id: JET2Messages.properties,v 1.1 2007/04/04 14:53:54 pelder Exp $
+#
+JET2Compiler_ErrorWritingJava=Error writing transformation loader Java class
+JET2Compiler_UnknownTagLibrary=Unknown tag library id {0}.
+JET2Compiler_UnknownAttribute=Unknown attribute: {0}
+JET2Compiler_TagRequiresContent=Tag must have content.
+JET2Compiler_MissingEndTag=No corresponding end tag </{0}> found.
+JET2Compiler_MissingFile=Unable to resolve include of file: {0}
+JET2Compiler_DeprecatedTag=Tag has been deprecated.
+JET2Compiler_EndDirectiveOutOfContext=The end directive may only occur after <%@include fail="alternative" ...%> and <%@start%>
+JET2Compiler_MissingStartTag=No corresponding start tag <{0}> found.
+JET2Compiler_ErrorRemovingMarkers=Unexpected error removing resource markers
+JET2Compiler_ErrorCreatingMarkers=Unexpected exception creating problem markers
+JET2Compiler_SameJavaClassAsOther=This template creates the same Java class {0} as {1}
+JET2Compiler_StartDirectiveOutOfContext=The start directive may only occur after an <%@include fail="alternative" ...%>
+JET2Compiler_TagCannotHaveContent=Tag can only occur as an empty tag.
+JET2Compiler_TagShouldBeEmptyFormat=Tag should be have format {0}
+JET2Compiler_DeprecatedAttribute=Use of deprecated attribute: {0}
+JET2Compiler_MissingDirectiveAttribute=The {0} directive requires an {1} attribute.
+JET2Compiler_MissingRequiredAttribute=Missing required attribute: {0}
+JET2Compiler_PrefixAlreadyAssigned=The namespace prefix "{0}" is already associated with the tag library {1}.
+
+ASTCompilerParseListener_UnsupportedDirective=Unsupported directive ''{0}''
+
+JET2Context_SuccessfulExecution=Successful Execution
+JET2Context_SuccessfulWithMessages=Successful execution with messages
+JET2Context_SuccessfulWithWarnings=Successful execution with warnings
+JET2Context_InvalidVariableName=Invalid variable name ''{0}''
+JET2Context_VariableNotFound=Variable is not defined: {0}
+JET2Context_ErrorsInExecution=Errors occurred during execution
+JET2Context_ExecutionCancelled=Execution cancelled
+JET2Context_CommittingActions=Committing actions
+JET2Context_CouldNotFindTemplate=Could not find template {0}
+
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/taglib/TagLibraryReferenceImpl.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/taglib/TagLibraryReferenceImpl.java
new file mode 100644
index 0000000..ea9c1d2
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/internal/taglib/TagLibraryReferenceImpl.java
@@ -0,0 +1,71 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagLibraryReferenceImpl.java,v 1.1 2007/04/04 14:53:54 pelder Exp $
+ */
+package org.eclipse.jet.internal.taglib;
+
+import org.eclipse.jet.taglib.TagLibrary;
+import org.eclipse.jet.taglib.TagLibraryReference;
+
+/**
+ * Standard implementation of TagLibraryReference.
+ */
+public class TagLibraryReferenceImpl implements TagLibraryReference
+{
+
+ private final String prefix;
+ private final String id;
+ private final boolean autoImport;
+
+ public TagLibraryReferenceImpl(String prefix, String id, boolean autoImport)
+ {
+ this.prefix = prefix;
+ this.id = id;
+ this.autoImport = autoImport;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.taglib.TagLibraryReference#getPrefix()
+ */
+ public String getPrefix()
+ {
+ return prefix;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.taglib.TagLibraryReference#getTagLibraryId()
+ */
+ public String getTagLibraryId()
+ {
+ return id;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.taglib.TagLibraryReference#getTagLibrary()
+ */
+ public TagLibrary getTagLibrary()
+ {
+ throw new UnsupportedOperationException("Unsupported"); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jet.taglib.TagLibraryReference#isAutoImport()
+ */
+ public boolean isAutoImport()
+ {
+ return autoImport;
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/CustomTagKind.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/CustomTagKind.java
new file mode 100644
index 0000000..fea4e26
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/CustomTagKind.java
@@ -0,0 +1,69 @@
+package org.eclipse.jet.taglib;
+
+
+/**
+ * An enumeration of the deferent kinds of tags.
+ *
+ */
+public final class CustomTagKind
+{
+
+ /**
+ * A tag declared in the tagLibrary extension using the 'tag' element.
+ */
+ public static final CustomTagKind OTHER = new CustomTagKind("OTHER"); //$NON-NLS-1$
+
+ /**
+ * A tag declared in the tagLibrary extension using the 'emptyTag' element.
+ * Empty tags must take the form of an XML empty tag (&lt;<i>tagName</i> ... /&gt;), and
+ * create any content or perform any actions based solely on the elements context and attribute values.
+ */
+ public static final CustomTagKind EMPTY = new CustomTagKind("EMPTY"); //$NON-NLS-1$
+
+ /**
+ * A tag declared in the tagLibrary extension using the 'functionTag' element.
+ * Function tags must take the form of an XML begin/end tag pair (&lt;tagName ... &;gt><i>content</i>&lt;/tagName&gt;).
+ * A function tag creates output by evaluating a function upon the elements content, its context and attribute values.
+ */
+ public static final CustomTagKind FUNCTION = new CustomTagKind("FUNCTION"); //$NON-NLS-1$
+
+ /**
+ * A tag declared in the tagLibrary extension using the 'iteratingTag' element.
+ * Iterating tags must take the form of an XML begin/end tag pair.
+ * An iterating tag creates output by repeating evaluating a condition based on the element's context and attributes.
+ * Each time the condition evaluates to <code>true</code>, the elements contents are written to the output.
+ */
+ public static final CustomTagKind ITERATING = new CustomTagKind("ITERATING"); //$NON-NLS-1$
+
+ /**
+ * A tag declared in the tagLibrary extension using the 'conditionalTag' element.
+ * Conditional tags must take the form of an XML begin/end tag pair.
+ * A conditional tag creates output by evaluating a condition based on the element's context and attributes.
+ * If the condition evaluates to <code>true</code>, the elements contents are written to the output.
+ */
+ public static final CustomTagKind CONDITIONAL = new CustomTagKind("CONDITIONAL"); //$NON-NLS-1$
+
+ /**
+ * A tag declared in the tagLibrary extension using the 'containerTag' element.
+ * Container tags must tag the form of an XML begin/end tag pari.
+ * A container tag preforms actions prior to its content being processed and
+ * after its content has been processed.
+ */
+ public static final CustomTagKind CONTAINER = new CustomTagKind("CONTAINER"); //$NON-NLS-1$
+
+ private final String display;
+
+ private CustomTagKind(String display)
+ {
+ this.display = display;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return display;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/JET2TagException.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/JET2TagException.java
new file mode 100644
index 0000000..51f1a24
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/JET2TagException.java
@@ -0,0 +1,70 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: JET2TagException.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.taglib;
+
+
+/**
+ * Encapsulate an execution error during tag execution.
+ *
+ */
+public class JET2TagException extends RuntimeException
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -2909288156948233977L;
+
+ /**
+ *
+ */
+ public JET2TagException()
+ {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @param message
+ */
+ public JET2TagException(String message)
+ {
+ super(message);
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public JET2TagException(String message, Throwable cause)
+ {
+ super(message, cause);
+ // TODO Auto-generated constructor stub
+ }
+
+ /**
+ * @param cause
+ */
+ public JET2TagException(Throwable cause)
+ {
+ super(cause);
+ // TODO Auto-generated constructor stub
+ }
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/RuntimeTagElement.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/RuntimeTagElement.java
new file mode 100644
index 0000000..7df1fc6
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/RuntimeTagElement.java
@@ -0,0 +1,96 @@
+package org.eclipse.jet.taglib;
+
+
+import org.eclipse.jet.JET2Context;
+import org.eclipse.jet.JET2Writer;
+
+
+/**
+ * Defines the execution behavior an XML Element in a JET2 compiled template.
+ * <P>
+ * Clients do not typically use the class directly.
+ * </P>
+ * <P>
+ * The tag has the following life cycle:
+ * <BL>
+ * <LI>Tag constructor is called.</LI>
+ * <LI>Parent tag is set via {@link #setRuntimeParent(RuntimeTagElement) setParent()}. If the tag has no parent, <code>null</code> is passed.</LI>
+ * <li>The tag context is set via {@link #setTagInfo(TagInfo)}.</li>
+ * <LI>The tag is readied for operation by a call to {@link #doStart(JET2Context, JET2Writer) doStart()}.</LI>
+ * <LI>If the element has body content (that is, it is not an empty tag, but a begin-tag/end-tag pair), then
+ * {@link #okToProcessBody() okToProcessBody()} is called.</LI>
+ * <LI>If {@link #okToProcessBody() okToProcessBody()} returns <code>true</code>, then {@link #handleBodyContent(JET2Writer) handleBodyContent()} is called.</LI>
+ * <LI>{@link #okToProcessBody() okToProcessBody()} and {@link #handleBodyContent(JET2Writer) handleBodyContent()} are called
+ * repeatedly until {@link #okToProcessBody() okToProcessBody()} returns <code>false</code>.</LI>
+ * <LI>Once body processing is complete, {@link #doEnd() doEnd()} is called.</LI>
+ * </BL>
+ * </P>
+ * <P>
+ * This interface is not intended to be implemented by clients.
+ * </P>
+ */
+public interface RuntimeTagElement
+{
+
+ /**
+ * Set the parent tag of the element. This method always called once, after construction,
+ * and before {@link #doStart(JET2Context, JET2Writer) ready()} is called.
+ * @param parentTag the parent tag, or <code>null</code> if the tag has no parent in the template.
+ */
+ public abstract void setRuntimeParent(RuntimeTagElement parentTag);
+
+ /**
+ * Provide the tag with information on its context within the template, include attribute values
+ * and position.
+ * @param tagInfo the tag information. Will never by <code>null</code>.
+ * @see TagInfo
+ */
+ public abstract void setTagInfo(TagInfo tagInfo);
+
+ /**
+ * Return the tag context set by {@link #setTagInfo(TagInfo)}.
+ * @return the tag context, or <code>null</code> if not yet set.
+ */
+ public abstract TagInfo getTagInfo();
+
+ /**
+ * Perform any actions associated with the start of the tag element.
+ * @param context
+ * @param out
+ */
+ public abstract void doStart(JET2Context context, JET2Writer out);
+
+ /**
+ * Perform any actions associated with the end of the tag element.
+ *
+ */
+ public abstract void doEnd();
+
+ /**
+ * Determine if the tag element's body should be processed.
+ * If the tag element has a body (even an empty body), then this method is called repeatedly until <code>false</code>
+ * is returned. Each time <code>true</code> is returned, {@link #handleBodyContent(JET2Writer)} is called.
+ *
+ * @return <code>true</code> if the body should be processed, <code>false</code> otherwise.
+ */
+ public abstract boolean okToProcessBody();
+
+ /**
+ * Handle the body content of the tag element.
+ * This method is called once everytime a call to {@link #okToProcessBody() okToProcessBody()} returns <code>true</code>.
+ * @param bodyContent the writer containing the body content. Will never by <code>null</code>.
+ */
+ public abstract void handleBodyContent(JET2Writer bodyContent);
+
+ /**
+ * Return the context passed to {@link #doStart(JET2Context, JET2Writer)}.
+ * @return the context
+ */
+ public abstract JET2Context getContext();
+
+ /**
+ * Return the writer passed to {@link #doStart(JET2Context, JET2Writer)}.
+ * @return the current output writer
+ */
+ public abstract JET2Writer getWriter();
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagAttributeDefinition.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagAttributeDefinition.java
new file mode 100644
index 0000000..14028fd
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagAttributeDefinition.java
@@ -0,0 +1,76 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagAttributeDefinition.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.taglib;
+
+
+/**
+ * Expose the details of a Custom Tag attribute
+ *
+ */
+public interface TagAttributeDefinition
+{
+
+ /**
+ * An attribute whose value is any legal string. Value is "string".
+ */
+ public static final String STRING_TYPE = "string"; //$NON-NLS-1$
+
+ /**
+ * An attribute whose value is either "true" or "false". Value is "boolean".
+ */
+ public static final String BOOLEAN_TYPE = "boolean"; //$NON-NLS-1$
+
+ /**
+ * An attribute whose value is an XPath expression. "Value is "xpath".
+ */
+ public static final String XPATH_TYPE = "xpath"; //$NON-NLS-1$
+
+ /**
+ * Return the attribute's name
+ * @return the attribute name
+ */
+ public abstract String getName();
+
+ /**
+ * Return the attribute's description if provided
+ * @return the description or an empty string.
+ */
+ public abstract String getDescription();
+
+ /**
+ * Return the attributes's type
+ * @return the attribute's type
+ * @see #BOOLEAN_TYPE
+ * @see #STRING_TYPE
+ * @see #XPATH_TYPE
+ */
+ public abstract String getType();
+
+ /**
+ * Test if the element is required
+ * @return <code>true</code> if the element is required, <code>false</code> otherwise.
+ */
+ public abstract boolean isRequired();
+
+ /**
+ * Test if the element has been deprecated
+ * @return <code>true</code> if the element has been deprecated, <code>false</code> otherwise.
+ */
+ public abstract boolean isDeprecated();
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagDefinition.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagDefinition.java
new file mode 100644
index 0000000..12e1bac
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagDefinition.java
@@ -0,0 +1,121 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagDefinition.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.taglib;
+
+
+import java.util.List;
+
+
+/**
+ * Expose the definition of a tag, as declared in a 'org.eclipse.jet.tagLibraries' extension.
+ * <P>
+ * This interface is not intended to be implemented by clients.
+ * </P>
+ */
+public interface TagDefinition
+{
+
+ /**
+ * Return the name of the tag as it is registered in the tag library
+ * @return the tag's name
+ */
+ public abstract String getName();
+
+ public abstract String getDescription();
+
+ /**
+ * Return the tag kind ({@link CustomTagKind}).
+ * @return the tag kind.
+ */
+ public abstract CustomTagKind getKind();
+
+ /**
+ * Return the definition of the named attribute
+ * @param name the attribute name
+ * @return the attribute definition, or <code>null</code> if the <code>name</code> is not
+ * an attribute of the named tag.
+ */
+ public abstract TagAttributeDefinition getAttributeDefinition(String name);
+
+ /**
+ * Return a list of attribute definitions for this tag
+ * @return a List of {@link TagAttributeDefinition} objects. The empty list is returned if there
+ * are no attribute definitions for this tag definition.
+ */
+ public abstract List getAttributeDefinitions();
+
+ /**
+ * Test if the tag is declared to be deprecated.
+ * @return <code>true</code> if the tag is deprecated.
+ */
+ public abstract boolean isDeprecated();
+
+ /**
+ * Test whether the tag requires a new writer for its contents.
+ * The following tag declaration will have the value set to <code>true</code>:
+ * <bl>
+ * <li><code>functionTag</code></li>
+ * <li><code>containerTag</code> with <code>processContents</code> set to <code>custom</code>.</li>
+ * <li><code>conditionalTag</code> with <code>processContents</code> set to <code>custom</code>.</li>
+ * <li><code>iteratingTag</code> with <code>processContents</code> set to <code>custom</code>.</li>
+ * </bl>
+ * @return <code>true</code> if a new writer is required, <code>false</code> otherwise.
+ */
+ public abstract boolean requiresNewWriter();
+
+ /**
+ * Test whether the tag is allowed to be specified in the empty tag form: &lt;tagName/&gt;.
+ * The following tag declarations will have the value set to <code>true</code>.
+ * <bl>
+ * <li><code>emptyTag</code></li>
+ * <li><code>containerTag</code> with <code>allowAsEmpty</code> set to <code>true</code>.</li>
+ * </bl>
+ * @return <code>true</code> if the tag may be expressed as a empty tag.
+ */
+ public abstract boolean isEmptyTagAllowed();
+
+ /**
+ * Test whether the tag is allowed to have content (even empty content). That is, this
+ * method tests whether a tag of the form:
+ * <blockquote>
+ * &lt;tagName&gt; ... &lt;/tagName&gt;
+ * </blockquote>
+ * The following tag declarations will have the value set to <code>true</code>:
+ * <bl>
+ * <li><code>functionTag</code></li>
+ * <li><code>containerTag</code></li>
+ * <li><code>conditionalTag</code></li>
+ * <li><code>iteratingTag</code></li>
+ * </bl>
+ * @return <code>true</code> if the tag may be expressed as a content tag.
+ */
+ public abstract boolean isContentAllowed();
+
+ /**
+ * Return the {@link TagLibrary} that contains this tag definition.
+ * @return a TagLibrary instance.
+ */
+ public abstract TagLibrary getTagLibrary();
+
+ /**
+ * Indicate whether the compiler should remove whitespace including the trailing
+ * new line from tags that occur on an otherwise empty line.
+ * @return <code>true</code> if such whitespace should be removed.
+ */
+ public abstract boolean removeWhenContainingLineIsEmpty();
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagFactory.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagFactory.java
new file mode 100644
index 0000000..bcae7e3
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagFactory.java
@@ -0,0 +1,33 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagFactory.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.taglib;
+
+
+/**
+ * Factory used by compiled JET2 templates to instantiate
+ * custom XML Elements.
+ * <p>
+ * This class is not typically used directly by clients.
+ * </p>
+ */
+public interface TagFactory
+{
+
+ RuntimeTagElement createRuntimeTag(String libraryId, String tagNCName, String tagQName, TagInfo tagInfo);
+
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagInfo.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagInfo.java
new file mode 100644
index 0000000..d6c80e6
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagInfo.java
@@ -0,0 +1,152 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagInfo.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.taglib;
+
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+/**
+ * Define contextual information for a custom tag. The class is immutable. The Tag context is
+ * the mechanism by which custom tag code accesses the tag parameters code in a template.
+ * <p>Contextual information includes:</p>
+ * <bl>
+ * <li>the tag location in the source template (line # and start, end offsets)</li>
+ * <li>the raw attribute values as specified in template.
+ * </bl>
+ * <p>
+ * <b>This class is instantiated in the compiled JET2 template. Clients would not normally instantiate
+ * instances of this class.</b>
+ * </p>
+ */
+public final class TagInfo
+{
+
+ private final int line;
+
+ private final Map attrMap;
+
+ private final String tagName;
+
+ private final int col;
+
+ public TagInfo(String tagName, int line, int col, String[] attrNames, String attrValues[])
+ {
+ this.tagName = tagName;
+ this.line = line;
+ this.col = col;
+
+ if (attrNames == null || attrValues == null)
+ {
+ throw new NullPointerException();
+ }
+ if (attrNames.length != attrValues.length)
+ {
+ throw new IllegalArgumentException();
+ }
+ // use Linked has map so that attributes retain their original order.
+ this.attrMap = new LinkedHashMap(attrNames.length);
+ for (int i = 0; i < attrNames.length; i++)
+ {
+ attrMap.put(attrNames[i], attrValues[i]);
+ }
+ }
+
+ /**
+ * Construct a TagInfo
+ * @param tagName
+ * @param line
+ * @param start
+ * @param end
+ * @param attrNames
+ * @param attrValues
+ * @deprecated Use {@link #TagInfo(String, int, int, String[], String[])} instead.
+ */
+ public TagInfo(String tagName, int line, int start, int end, String[] attrNames, String[] attrValues)
+ {
+ this(tagName, line, 1, attrNames, attrValues);
+ }
+
+ /**
+ * Return the one-based line number of the start of the tag.
+ * @return Returns the line.
+ */
+ public final int getLine()
+ {
+ return line;
+ }
+
+ /**
+ * Return the value of an attribute.
+ * @param name the attribute name. Cannot be <code>null</code>.
+ * @return the attribute value, or <code>null</code> if the attribute was not set on the tag.
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ public final String getAttribute(String name)
+ {
+ if (name == null)
+ {
+ throw new NullPointerException();
+ }
+ return (String)attrMap.get(name);
+ }
+
+ /**
+ * Test whether an attribute value was set.
+ * @param name the attribute name. Cannot be <code>null</code>.
+ * @return <code>true</code> if the attribute was set, <code>false</code> otherwise.
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ public final boolean hasAttribute(String name)
+ {
+ return attrMap.containsKey(name);
+ }
+
+ public String toString()
+ {
+ return "[" + line + ":" + col + " " + tagName + " " + attrMap + "]"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ }
+
+ /**
+ * Return the tag name, as specified in the input, complete with namespace prefix.
+ * @return Returns the tagName.
+ */
+ public final String getTagName()
+ {
+ return tagName;
+ }
+
+ /**
+ * Return an array of attribute names
+ * @return an possibly empty array of String values.
+ */
+ public final String[] getAttributeNames()
+ {
+ return (String[])attrMap.keySet().toArray(new String[attrMap.size()]);
+ }
+
+ /**
+ * Return the one-based column number of the start of the tag withing the tag's start line.
+ * @return Returns the col.
+ */
+ public final int getCol()
+ {
+ return col;
+ }
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibrary.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibrary.java
new file mode 100644
index 0000000..3c66147
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibrary.java
@@ -0,0 +1,71 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagLibrary.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+
+package org.eclipse.jet.taglib;
+
+
+
+
+/**
+ * Provide access to the tags in a tag library.
+ *
+ * <P>
+ * This interface is not intended to be implemented by clients.
+ * </P>
+ */
+public interface TagLibrary
+{
+
+ /**
+ * Return the string identifier of the tag library.
+ * @return the tag library id
+ */
+ public abstract String getLibraryId();
+
+ public abstract String getLibraryName();
+
+ public abstract String getDefaultPrefix();
+
+ public abstract String getDescription();
+
+ /**
+ * Return the {@link TagDefinition} for the named tag.
+ * @param name the name of a tag in the tag library.
+ * @return the definitions or <code>null</code> if the named tag is in the library
+ * @throws NullPointerException if <code>name</code> is null.
+ */
+ public abstract TagDefinition getTagDefinition(String name);
+
+ /**
+ * Return a sort array of tags in the library
+ * @return an array of Strings; an empty array is returned if the library has no tags.
+ */
+ public abstract String[] getTagNames();
+
+ /**
+ * Test if the named tag is in the tag library.
+ * @param tagNCName
+ * @return if the tag is defined by the library
+ */
+ public abstract boolean hasTag(String tagNCName);
+
+ /**
+ * Test if the tag library has been deprecated.
+ * @return <code>true</code> if the library has been deprecated, <code>false</code> otherwise.
+ */
+ public abstract boolean isDeprecated();
+}
diff --git a/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibraryReference.java b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibraryReference.java
new file mode 100644
index 0000000..ad32aa0
--- /dev/null
+++ b/plugins/org.eclipse.jet.core/src/org/eclipse/jet/taglib/TagLibraryReference.java
@@ -0,0 +1,48 @@
+/**
+ * <copyright>
+ *
+ * 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 - Initial API and implementation
+ *
+ * </copyright>
+ *
+ * $Id: TagLibraryReference.java,v 1.1 2007/04/04 14:53:53 pelder Exp $
+ */
+package org.eclipse.jet.taglib;
+
+/**
+ * Represent a reference to a tag library by a template or a transform.
+ */
+public interface TagLibraryReference
+{
+
+ /**
+ * Return the prefix that is mapped to the tag library.
+ * @return a string
+ */
+ public abstract String getPrefix();
+
+ /**
+ * Return the tag library id.
+ * @return a string
+ */
+ public abstract String getTagLibraryId();
+
+ /**
+ * Return the TagLibrary description object.
+ * @return an TagLibrary instance
+ */
+ public abstract TagLibrary getTagLibrary();
+
+ /**
+ * Test whether the tag library is automatically imported bundle templates.
+ * @return <code>true</code> if the library is an auto imported library.
+ */
+ public abstract boolean isAutoImport();
+}