3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 import SCons
.Scanner
.IDL
29 import SCons
.Tool
.JavaCommon
33 # Adding trace=trace to any of the parse_jave() calls below will cause
34 # the parser to spit out trace messages of the tokens it sees and the
35 # attendant transitions.
37 def trace(token
, newstate
) -> None:
38 from SCons
.Debug
import Trace
39 statename
= newstate
.__class
__.__name
__
40 Trace('token = %s, state = %s\n' % (repr(token
), statename
))
42 class parse_javaTestCase(unittest
.TestCase
):
44 def test_bare_bones(self
) -> None:
45 """Test a bare-bones class"""
53 public static void main(String[] args)
56 /* This tests a former bug where strings would eat later code. */
57 String hello1 = new String("Hello, world!");
63 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
64 assert pkg_dir
== os
.path
.join('com', 'sub', 'bar'), pkg_dir
65 assert classes
== ['Foo'], classes
68 def test_file_parser(self
) -> None:
69 """Test the file parser"""
75 public static void main(String[] args)
77 /* This tests that unicode is handled . */
78 String hello1 = new String("ఎత్తువెడల్పు");
79 /* and even smart quotes “like this” ‘and this’ */
83 file_name
= 'test_file_parser.java'
84 with
open(file_name
, 'w', encoding
='UTF-8') as jf
:
87 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java_file(file_name
)
88 if os
.path
.exists(file_name
):
90 assert pkg_dir
== os
.path
.join('com', 'sub', 'bar'), pkg_dir
91 assert classes
== ['Foo'], classes
94 def test_dollar_sign(self
) -> None:
95 """Test class names with $ in them"""
99 public void new$rand () {}
102 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
103 assert pkg_dir
is None, pkg_dir
104 assert classes
== ['BadDep'], classes
108 def test_inner_classes(self
) -> None:
109 """Test parsing various forms of inner classes"""
116 public void execute();
121 Test implements Listener {
125 public void execute() {
126 System.out.println("In Inner");
130 String s1 = "class A";
131 String s2 = "new Listener() { }";
133 /* new Listener() { } */
137 Inner2() { Listener l = new Listener(); }
140 /* Make sure this class doesn't get interpreted as an inner class of the previous one, when "new" is used in the previous class. */
145 public static void main(String[] args) {
151 public void execute() {
152 use(new Listener( ) {
153 public void execute() {
154 System.out.println("Inside execute()");
163 void use(Listener l) {
171 public void execute() {
178 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.4')
179 assert pkg_dir
is None, pkg_dir
193 assert classes
== expect
, classes
195 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.5')
196 assert pkg_dir
is None, pkg_dir
210 assert classes
== expect
, (expect
, classes
)
212 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '5')
213 assert pkg_dir
is None, pkg_dir
227 assert classes
== expect
, (expect
, classes
)
231 def test_comments(self
) -> None:
232 """Test a class with comments"""
237 import java.rmi.Naming;
238 import java.rmi.RemoteException;
239 import java.rmi.RMISecurityManager;
240 import java.rmi.server.UnicastRemoteObject;
242 public class Example1 extends UnicastRemoteObject implements Hello {
244 public Example1() throws RemoteException {
248 public String sayHello() {
249 return "Hello World!";
252 public static void main(String args[]) {
253 if (System.getSecurityManager() == null) {
254 System.setSecurityManager(new RMISecurityManager());
258 Example1 obj = new Example1();
260 Naming.rebind("//myhost/HelloServer", obj);
262 System.out.println("HelloServer bound in registry");
263 } catch (Exception e) {
264 System.out.println("Example1 err: " + e.getMessage());
271 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
272 assert pkg_dir
== os
.path
.join('com', 'sub', 'foo'), pkg_dir
273 assert classes
== ['Example1'], classes
276 def test_arrays(self
) -> None:
277 """Test arrays of class instances"""
281 MyClass abc = new MyClass();
282 MyClass xyz = new MyClass();
283 MyClass _array[] = new MyClass[] {
290 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
291 assert pkg_dir
is None, pkg_dir
292 assert classes
== ['Test'], classes
296 def test_backslash(self
) -> None:
297 """Test backslash handling"""
302 private class MyInternal
305 private final static String PATH = "images\\\\";
309 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
310 assert pkg_dir
is None, pkg_dir
311 assert classes
== ['MyTabs$MyInternal', 'MyTabs'], classes
314 def test_enum(self
) -> None:
315 """Test the Java 1.5 enum keyword"""
322 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
323 assert pkg_dir
== 'p', pkg_dir
324 assert classes
== ['a'], classes
327 def test_anon_classes(self
) -> None:
328 """Test anonymous classes"""
331 public abstract class TestClass
333 public void completed()
346 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
347 assert pkg_dir
is None, pkg_dir
348 assert classes
== ['TestClass$1', 'TestClass$2', 'TestClass'], classes
351 def test_closing_bracket(self
) -> None:
352 """Test finding a closing bracket instead of an anonymous class"""
356 public static void main(String[] args) {
357 Foo[] fooArray = new Foo[] { new Foo() };
364 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
365 assert pkg_dir
is None, pkg_dir
366 assert classes
== ['TestSCons', 'Foo'], classes
369 def test_dot_class_attributes(self
) -> None:
370 """Test handling ".class" attributes"""
373 public class Test extends Object
376 Class c = Object[].class;
377 Object[] s = new Object[] {};
382 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
383 assert classes
== ['Test'], classes
388 public void F(Object[] o) {
389 F(new Object[] {Object[].class});
391 public void G(Object[] o) {
398 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input)
399 assert pkg_dir
is None, pkg_dir
400 assert classes
== ['A$B', 'A'], classes
402 def test_anonymous_classes_with_parentheses(self
) -> None:
403 """Test finding anonymous classes marked by parentheses"""
409 public static void main(String[] args) {
412 public String toString() {
417 public String toString() {
425 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.4')
426 assert classes
== ['Foo$1', 'Foo$2', 'Foo'], classes
428 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.5')
429 assert classes
== ['Foo$1', 'Foo$1$1', 'Foo'], classes
431 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '6')
432 assert classes
== ['Foo$1', 'Foo$1$1', 'Foo'], classes
436 def test_nested_anonymous_inner_classes(self
) -> None:
437 """Test finding nested anonymous inner classes"""
440 // import java.util.*;
442 public class NestedExample
444 public NestedExample()
446 Thread t = new Thread() {
449 Thread t = new Thread() {
452 try {Thread.sleep(200);}
453 catch (Exception e) {}
458 try {Thread.sleep(200);}
459 catch (Exception e) {}
466 public static void main(String argv[])
468 NestedExample e = new NestedExample();
473 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.4')
474 expect
= [ 'NestedExample$1', 'NestedExample$2', 'NestedExample' ]
475 assert expect
== classes
, (expect
, classes
)
477 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.5')
478 expect
= [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
479 assert expect
== classes
, (expect
, classes
)
481 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '6')
482 expect
= [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
483 assert expect
== classes
, (expect
, classes
)
485 def test_lambda_after_new(self
) -> None:
486 """Test lamdas after new"""
489 // import java.util.*;
491 public class LamdaExample
494 public void testFunc (int arg1, String arg2, Runnable lambda){
496 public LamdaExample()
501 // Lambda symbol is after new, and used curly braces so
502 // we should not parse this as a new class.
508 public static void main(String argv[])
510 LamdaExample e = new LamdaExample();
514 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.4')
515 expect
= [ 'LamdaExample' ]
516 assert expect
== classes
, (expect
, classes
)
518 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.8')
519 expect
= [ 'LamdaExample' ]
520 assert expect
== classes
, (expect
, classes
)
522 def test_private_inner_class_instantiation(self
) -> None:
523 """Test anonymous inner class generated by private instantiation"""
541 # This is what we *should* generate, apparently due to the
542 # private instantiation of the inner class, but don't today.
543 #expect = [ 'test$1', 'test$inner', 'test' ]
545 # What our parser currently generates, which doesn't match
546 # what the Java compiler actually generates.
547 expect
= [ 'test$inner', 'test' ]
549 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.4')
550 assert expect
== classes
, (expect
, classes
)
552 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.5')
553 assert expect
== classes
, (expect
, classes
)
555 def test_floating_point_numbers(self
) -> None:
556 """Test floating-point numbers in the input stream"""
564 Object anonymousInnerOK = new Runnable() { public void run () {} };
569 class InnerOK { InnerOK () { } }
572 System.out.println("a number: " + 1000.0 + "");
578 Object anonymousInnerBAD = new Runnable() { public void run () {} };
583 class InnerBAD { InnerBAD () { } }
587 expect
= ['Broken$1', 'Broken$InnerOK', 'Broken$2', 'Broken$InnerBAD', 'Broken']
589 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.4')
590 assert expect
== classes
, (expect
, classes
)
592 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.5')
593 assert expect
== classes
, (expect
, classes
)
596 def test_genercis(self
) -> None:
597 """Test that generics don't interfere with detecting anonymous classes"""
600 import java.util.Date;
601 import java.util.Comparator;
607 Comparator<Date> comp = new Comparator<Date>()
609 static final long serialVersionUID = 1L;
610 public int compare(Date lhs, Date rhs)
619 expect
= [ 'Foo$1', 'Foo' ]
621 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.6')
622 assert expect
== classes
, (expect
, classes
)
624 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '6')
625 assert expect
== classes
, (expect
, classes
)
628 def test_in_function_class_declaration(self
) -> None:
630 Test that implementing a class in a function call doesn't confuse SCons.
636 public class AnonDemo {
638 public static void main(String[] args) {
639 new AnonDemo().execute();
642 public void execute() {
643 Foo bar = new Foo(new Foo() {
645 public int getX() { return this.x; }
648 public int getX() { return this.x; }
652 public abstract class Foo {
654 public abstract int getX();
664 expect
= ['AnonDemo$1',
668 pkg_dir
, classes
= SCons
.Tool
.JavaCommon
.parse_java(input, '1.8')
669 assert expect
== classes
, (expect
, classes
)
671 def test_jdk_globs(self
) -> None:
673 Verify that the java path globs work with specific examples.
676 from SCons
.Tool
.JavaCommon
import (
677 java_linux_include_dirs_glob
,
678 java_linux_version_include_dirs_glob
,
680 java_win32_version_dir_glob
,
681 java_macos_include_dir_glob
,
682 java_macos_version_include_dir_glob
,
687 (r
'C:/Program Files/Java/jdk1.8.0_201/bin', '1.8.0'),
688 (r
'C:/Program Files/Java/jdk-11.0.2/bin', '11.0.2'),
689 (r
'C:/Program Files/AdoptOpenJDK/jdk-16.0.1.9-hotspot/bin', '16.0.1'),
690 (r
'C:/Program Files/Microsoft/jdk-17.0.0.35-hotspot/bin', '17.0.0'),
691 (r
'C:/Program Files/OpenJDK/openjdk-11.0.13_8/bin', '11.0.13'),
692 (r
'C:/Program Files/RedHat/java-1.8.0-openjdk-1.8.0.312-1/bin', '1.8.0'),
693 (r
'C:/Program Files/RedHat/java-11-openjdk-11.0.13-1/bin', '11.0.13'),
696 for (wjd
, version
) in win_java_dirs
:
697 if not fnmatch
.fnmatch(wjd
, java_win32_dir_glob
):
699 "Didn't properly match %s with pattern %s"
700 % (wjd
, java_win32_dir_glob
)
702 if not fnmatch
.fnmatch(wjd
, java_win32_version_dir_glob
% version
):
704 "Didn't properly match %s with version (%s) specific pattern %s"
705 % (wjd
, version
, java_win32_version_dir_glob
% version
)
708 non_win_java_include_dirs
= [
710 '/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64/include',
713 ('/usr/lib/jvm/java-1.8.0-openjdk-amd64/include', '1.8.0'),
714 ('/usr/lib/jvm/java-8-openjdk-amd64/include', '8'),
717 # Test non-windows/non-macos globs
718 for (wjd
, version
) in non_win_java_include_dirs
:
721 for jlig
in java_linux_include_dirs_glob
:
722 globs_tried
.append(jlig
)
724 if fnmatch
.fnmatch(wjd
, jlig
):
730 "Didn't properly match %s with pattern %s" % (wjd
, globs_tried
)
735 for jlvig
in java_linux_version_include_dirs_glob
:
736 globs_tried
.append(jlvig
% version
)
737 if fnmatch
.fnmatch(wjd
, jlvig
% version
):
743 "Didn't properly match %s with version (%s) specific pattern %s"
744 % (wjd
, version
, globs_tried
)
750 # ('/System/Library/Frameworks/JavaVM.framework/Headers/', None),
752 '/System/Library/Frameworks/JavaVM.framework/Versions/11.0.2/Headers/',
757 if not fnmatch
.fnmatch(
758 '/System/Library/Frameworks/JavaVM.framework/Headers/',
759 java_macos_include_dir_glob
,
762 "Didn't properly match %s with pattern %s"
764 '/System/Library/Frameworks/JavaVM.framework/Headers/',
765 java_macos_include_dir_glob
,
769 for (wjd
, version
) in macos_java_dirs
:
770 if not fnmatch
.fnmatch(wjd
, java_macos_version_include_dir_glob
% version
):
772 "Didn't properly match %s with version (%s) specific pattern %s"
773 % (wjd
, version
, java_macos_version_include_dir_glob
% version
)
777 if __name__
== "__main__":
782 # indent-tabs-mode:nil
784 # vim: set expandtab tabstop=4 shiftwidth=4: