diff --git a/CHANGES b/CHANGES
index b6922d253967f71e48dd415615da2db9ec609289..2031ea2c40458906b3c6f7e74d75a9fb843039a9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,6 @@
 0.2.3
+- added support for Jython 2.5.
+
 - cache module now uses Beaker's clsmap to get at 
 container classes, so cache types such as 
 "ext:google", "ext:sqla", etc. are available.  
diff --git a/lib/mako/_ast_util.py b/lib/mako/_ast_util.py
index 728eef881971f2fe27f3a43f4470abd06c106200..968084b55f88098eec28080dbd37982a140adab1 100644
--- a/lib/mako/_ast_util.py
+++ b/lib/mako/_ast_util.py
@@ -25,8 +25,37 @@
     :copyright: Copyright 2008 by Armin Ronacher.
     :license: Python License.
 """
+import sys
 from _ast import *
 
+if sys.platform.startswith('java'):
+    import array
+
+    ast_list = array.ArrayType
+    get_symbol_key = lambda op: op
+        
+    def get_class_name(t):
+        result = t.__class__.__name__
+        if result in ("org.python.antlr.ast.expr_contextType",
+                "org.python.antlr.ast.boolopType",
+                "org.python.antlr.ast.unaryopType",
+                "org.python.antlr.ast.cmpopType",
+                "org.python.antlr.ast.operatorType"):
+            result = str(t)
+        else:
+            result = result.split(".")[-1]
+            if result.endswith("Type"):
+                result = result[:-4]
+            if result == "Unicode":
+                # XXX: likely unecessary now
+                result = "Str"
+        return result
+
+else:
+    ast_list = list
+    get_symbol_key = type
+    get_class_name = lambda node: node.__class__.__name__
+
 
 BOOLOP_SYMBOLS = {
     And:        'and',
@@ -109,7 +138,7 @@ def dump(node):
             return '%s(%s)' % (node.__class__.__name__,
                                ', '.join('%s=%s' % (a, _format(b))
                                          for a, b in iter_fields(node)))
-        elif isinstance(node, list):
+        elif isinstance(node, ast_list):
             return '[%s]' % ', '.join(_format(x) for x in node)
         return repr(node)
     if not isinstance(node, AST):
@@ -190,7 +219,7 @@ def iter_child_nodes(node):
     for name, field in iter_fields(node):
         if isinstance(field, AST):
             yield field
-        elif isinstance(field, list):
+        elif isinstance(field, ast_list):
             for item in field:
                 if isinstance(item, AST):
                     yield item
@@ -262,7 +291,7 @@ class NodeVisitor(object):
         exists for this node.  In that case the generic visit function is
         used instead.
         """
-        method = 'visit_' + node.__class__.__name__
+        method = 'visit_' + get_class_name(node)
         return getattr(self, method, None)
 
     def visit(self, node):
@@ -275,7 +304,7 @@ class NodeVisitor(object):
     def generic_visit(self, node):
         """Called if no explicit visitor function exists for a node."""
         for field, value in iter_fields(node):
-            if isinstance(value, list):
+            if isinstance(value, ast_list):
                 for item in value:
                     if isinstance(item, AST):
                         self.visit(item)
@@ -321,7 +350,7 @@ class NodeTransformer(NodeVisitor):
     def generic_visit(self, node):
         for field, old_value in iter_fields(node):
             old_value = getattr(node, field, None)
-            if isinstance(old_value, list):
+            if isinstance(old_value, ast_list):
                 new_values = []
                 for value in old_value:
                     if isinstance(value, AST):
@@ -422,7 +451,7 @@ class SourceGenerator(NodeVisitor):
     def visit_AugAssign(self, node):
         self.newline()
         self.visit(node.target)
-        self.write(BINOP_SYMBOLS[type(node.op)] + '=')
+        self.write(BINOP_SYMBOLS[get_symbol_key(node.op)] + '=')
         self.visit(node.value)
 
     def visit_ImportFrom(self, node):
@@ -698,7 +727,7 @@ class SourceGenerator(NodeVisitor):
     def visit_BinOp(self, node):
         self.write('(')
         self.visit(node.left)
-        self.write(' %s ' % BINOP_SYMBOLS[type(node.op)])
+        self.write(' %s ' % BINOP_SYMBOLS[get_symbol_key(node.op)])
         self.visit(node.right)
         self.write(')')
 
@@ -706,7 +735,7 @@ class SourceGenerator(NodeVisitor):
         self.write('(')
         for idx, value in enumerate(node.values):
             if idx:
-                self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)])
+                self.write(' %s ' % BOOLOP_SYMBOLS[get_symbol_key(node.op)])
             self.visit(value)
         self.write(')')
 
@@ -714,13 +743,13 @@ class SourceGenerator(NodeVisitor):
         self.write('(')
         self.visit(node.left)
         for op, right in zip(node.ops, node.comparators):
-            self.write(' %s ' % CMPOP_SYMBOLS[type(op)])
+            self.write(' %s ' % CMPOP_SYMBOLS[get_symbol_key(op)])
             self.visit(right)
         self.write(')')
 
     def visit_UnaryOp(self, node):
         self.write('(')
-        op = UNARYOP_SYMBOLS[type(node.op)]
+        op = UNARYOP_SYMBOLS[get_symbol_key(node.op)]
         self.write(op)
         if op == 'not':
             self.write(' ')
diff --git a/lib/mako/pyparser.py b/lib/mako/pyparser.py
index 644afda94b428952da98f0924014ecb6a7340c09..cc9c04e2e7fd8b3f4f8ea9cc4b23e80fa3852077 100644
--- a/lib/mako/pyparser.py
+++ b/lib/mako/pyparser.py
@@ -17,13 +17,14 @@ from mako import exceptions, util
 # words that cannot be assigned to (notably smaller than the total keys in __builtins__)
 reserved = util.Set(['True', 'False', 'None'])
 
-new_ast = sys.version_info > (2, 5)
+jython = sys.platform.startswith('java')
 
-if new_ast:
+try:
     import _ast
     util.restore__ast(_ast)
     import _ast_util
-else:
+except ImportError:
+    _ast = None
     from compiler import parse as compiler_parse
     from compiler import visitor
 
@@ -31,7 +32,7 @@ else:
 def parse(code, mode='exec', **exception_kwargs):
     """Parse an expression into AST"""
     try:
-        if new_ast:
+        if _ast:
             return _ast_util.parse(code, '<unknown>', mode)
         else:
             return compiler_parse(code, mode)
@@ -39,7 +40,7 @@ def parse(code, mode='exec', **exception_kwargs):
         raise exceptions.SyntaxException("(%s) %s (%s)" % (e.__class__.__name__, str(e), repr(code[0:50])), **exception_kwargs)
 
 
-if new_ast:
+if _ast:
     class FindIdentifiers(_ast_util.NodeVisitor):
         def __init__(self, listener, **exception_kwargs):
             self.in_function = False
@@ -90,7 +91,10 @@ if new_ast:
             for statement in node.orelse:
                 self.visit(statement)
         def visit_Name(self, node):
-            if isinstance(node.ctx, _ast.Store):
+            if jython:
+                if node.ctx == _ast.Store:
+                    self._add_declared(node.id)
+            elif isinstance(node.ctx, _ast.Store):
                 self._add_declared(node.id)
             if node.id not in reserved and node.id not in self.listener.declared_identifiers and node.id not in self.local_ident_stack:
                 self.listener.undeclared_identifiers.add(node.id)