~ubuntu-branches/ubuntu/utopic/scons/utopic-proposed

« back to all changes in this revision

Viewing changes to engine/SCons/Tool/JavaCommon.py

  • Committer: Bazaar Package Importer
  • Author(s): Mark Brown
  • Date: 2004-08-24 08:57:22 UTC
  • mfrom: (0.2.1 upstream) (1.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040824085722-hfk4f0pjbyu0ebxv
Tags: 0.96.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""SCons.Tool.JavaCommon
 
2
 
 
3
Stuff for processing Java.
 
4
 
 
5
"""
 
6
 
 
7
#
 
8
# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
 
9
#
 
10
# Permission is hereby granted, free of charge, to any person obtaining
 
11
# a copy of this software and associated documentation files (the
 
12
# "Software"), to deal in the Software without restriction, including
 
13
# without limitation the rights to use, copy, modify, merge, publish,
 
14
# distribute, sublicense, and/or sell copies of the Software, and to
 
15
# permit persons to whom the Software is furnished to do so, subject to
 
16
# the following conditions:
 
17
#
 
18
# The above copyright notice and this permission notice shall be included
 
19
# in all copies or substantial portions of the Software.
 
20
#
 
21
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
 
22
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 
23
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
24
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
25
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
26
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
27
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
28
#
 
29
 
 
30
__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/JavaCommon.py 0.96.1.D001 2004/08/23 09:55:29 knight"
 
31
 
 
32
import os
 
33
import os.path
 
34
import re
 
35
import string
 
36
 
 
37
java_parsing = 1
 
38
 
 
39
if java_parsing:
 
40
    # Parse Java files for class names.
 
41
    #
 
42
    # This is a really cool parser from Charles Crain
 
43
    # that finds appropriate class names in Java source.
 
44
 
 
45
    # A regular expression that will find, in a java file:  newlines;
 
46
    # any alphanumeric token (keyword, class name, specifier); open or
 
47
    # close brackets; a single-line comment "//"; the multi-line comment
 
48
    # begin and end tokens /* and */; single or double quotes; and
 
49
    # single or double quotes preceeded by a backslash.
 
50
    _reToken = re.compile(r'(\n|//|\\[\'"]|[\'"\{\}]|[A-Za-z_][\w\.]*|' +
 
51
                          r'/\*|\*/)')
 
52
 
 
53
    class OuterState:
 
54
        """The initial state for parsing a Java file for classes,
 
55
        interfaces, and anonymous inner classes."""
 
56
        def __init__(self):
 
57
            self.listClasses = []
 
58
            self.listOutputs = []
 
59
            self.stackBrackets = []
 
60
            self.brackets = 0
 
61
            self.nextAnon = 1
 
62
            self.package = None
 
63
 
 
64
        def __getClassState(self):
 
65
            try:
 
66
                return self.classState
 
67
            except AttributeError:
 
68
                ret = ClassState(self)
 
69
                self.classState = ret
 
70
                return ret
 
71
 
 
72
        def __getPackageState(self):
 
73
            try:
 
74
                return self.packageState
 
75
            except AttributeError:
 
76
                ret = PackageState(self)
 
77
                self.packageState = ret
 
78
                return ret
 
79
 
 
80
        def __getAnonClassState(self):
 
81
            try:
 
82
                return self.anonState
 
83
            except AttributeError:
 
84
                ret = SkipState(1, AnonClassState(self))
 
85
                self.anonState = ret
 
86
                return ret
 
87
 
 
88
        def __getSkipState(self):
 
89
            try:
 
90
                return self.skipState
 
91
            except AttributeError:
 
92
                ret = SkipState(1, self)
 
93
                self.skipState = ret
 
94
                return ret
 
95
 
 
96
        def parseToken(self, token):
 
97
            if token[:2] == '//':
 
98
                return IgnoreState('\n', self)
 
99
            elif token == '/*':
 
100
                return IgnoreState('*/', self)
 
101
            elif token == '{':
 
102
                self.brackets = self.brackets + 1
 
103
            elif token == '}':
 
104
                self.brackets = self.brackets - 1
 
105
                if len(self.stackBrackets) and \
 
106
                   self.brackets == self.stackBrackets[-1]:
 
107
                    self.listOutputs.append(string.join(self.listClasses, '$'))
 
108
                    self.listClasses.pop()
 
109
                    self.stackBrackets.pop()
 
110
            elif token == '"' or token == "'":
 
111
                return IgnoreState(token, self)
 
112
            elif token == "new":
 
113
                # anonymous inner class
 
114
                if len(self.listClasses) > 0:
 
115
                    return self.__getAnonClassState()
 
116
                return self.__getSkipState() # Skip the class name
 
117
            elif token == 'class' or token == 'interface':
 
118
                if len(self.listClasses) == 0:
 
119
                    self.nextAnon = 1
 
120
                self.stackBrackets.append(self.brackets)
 
121
                return self.__getClassState()
 
122
            elif token == 'package':
 
123
                return self.__getPackageState()
 
124
            return self
 
125
 
 
126
        def addAnonClass(self):
 
127
            """Add an anonymous inner class"""
 
128
            clazz = self.listClasses[0]
 
129
            self.listOutputs.append('%s$%d' % (clazz, self.nextAnon))
 
130
            self.brackets = self.brackets + 1
 
131
            self.nextAnon = self.nextAnon + 1
 
132
 
 
133
        def setPackage(self, package):
 
134
            self.package = package
 
135
 
 
136
    class AnonClassState:
 
137
        """A state that looks for anonymous inner classes."""
 
138
        def __init__(self, outer_state):
 
139
            # outer_state is always an instance of OuterState
 
140
            self.outer_state = outer_state
 
141
            self.tokens_to_find = 2
 
142
        def parseToken(self, token):
 
143
            # This is an anonymous class if and only if the next token is a bracket
 
144
            if token == '{':
 
145
                self.outer_state.addAnonClass()
 
146
            return self.outer_state
 
147
 
 
148
    class SkipState:
 
149
        """A state that will skip a specified number of tokens before
 
150
        reverting to the previous state."""
 
151
        def __init__(self, tokens_to_skip, old_state):
 
152
            self.tokens_to_skip = tokens_to_skip
 
153
            self.old_state = old_state
 
154
        def parseToken(self, token):
 
155
            self.tokens_to_skip = self.tokens_to_skip - 1
 
156
            if self.tokens_to_skip < 1:
 
157
                return self.old_state
 
158
            return self
 
159
 
 
160
    class ClassState:
 
161
        """A state we go into when we hit a class or interface keyword."""
 
162
        def __init__(self, outer_state):
 
163
            # outer_state is always an instance of OuterState
 
164
            self.outer_state = outer_state
 
165
        def parseToken(self, token):
 
166
            # the next non-whitespace token should be the name of the class
 
167
            if token == '\n':
 
168
                return self
 
169
            self.outer_state.listClasses.append(token)
 
170
            return self.outer_state
 
171
 
 
172
    class IgnoreState:
 
173
        """A state that will ignore all tokens until it gets to a
 
174
        specified token."""
 
175
        def __init__(self, ignore_until, old_state):
 
176
            self.ignore_until = ignore_until
 
177
            self.old_state = old_state
 
178
        def parseToken(self, token):
 
179
            if self.ignore_until == token:
 
180
                return self.old_state
 
181
            return self
 
182
 
 
183
    class PackageState:
 
184
        """The state we enter when we encounter the package keyword.
 
185
        We assume the next token will be the package name."""
 
186
        def __init__(self, outer_state):
 
187
            # outer_state is always an instance of OuterState
 
188
            self.outer_state = outer_state
 
189
        def parseToken(self, token):
 
190
            self.outer_state.setPackage(token)
 
191
            return self.outer_state
 
192
 
 
193
    def parse_java_file(fn):
 
194
        return parse_java(open(fn, 'r').read())
 
195
 
 
196
    def parse_java(contents):
 
197
        """Parse a .java file and return a double of package directory,
 
198
        plus a list of .class files that compiling that .java file will
 
199
        produce"""
 
200
        package = None
 
201
        initial = OuterState()
 
202
        currstate = initial
 
203
        for token in _reToken.findall(contents):
 
204
            # The regex produces a bunch of groups, but only one will
 
205
            # have anything in it.
 
206
            currstate = currstate.parseToken(token)
 
207
        if initial.package:
 
208
            package = string.replace(initial.package, '.', os.sep)
 
209
        return (package, initial.listOutputs)
 
210
 
 
211
else:
 
212
    # Don't actually parse Java files for class names.
 
213
    #
 
214
    # We might make this a configurable option in the future if
 
215
    # Java-file parsing takes too long (although it shouldn't relative
 
216
    # to how long the Java compiler itself seems to take...).
 
217
 
 
218
    def parse_java_file(fn):
 
219
        """ "Parse" a .java file.
 
220
 
 
221
        This actually just splits the file name, so the assumption here
 
222
        is that the file name matches the public class name, and that
 
223
        the path to the file is the same as the package name.
 
224
        """
 
225
        return os.path.split(file)