class_dependency.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # Lint as: python3
  2. # Copyright 2020 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """Implementation of the graph module for a [Java class] dependency graph."""
  6. import re
  7. from typing import Set, Tuple
  8. import graph
  9. import class_json_consts
  10. # Matches w/o parens: (some.package.name).(class)$($optional$nested$class)
  11. JAVA_CLASS_FULL_NAME_REGEX = re.compile(
  12. r'^(?P<package>.*)\.(?P<class_name>.*?)(\$(?P<nested>.*))?$')
  13. def java_class_params_to_key(package: str, class_name: str):
  14. """Returns the unique key created from a package and class name."""
  15. return f'{package}.{class_name}'
  16. def split_nested_class_from_key(key: str) -> Tuple[str, str]:
  17. """Splits a jdeps class name into its key and nested class, if any."""
  18. re_match = JAVA_CLASS_FULL_NAME_REGEX.match(key)
  19. package = re_match.group('package')
  20. class_name = re_match.group('class_name')
  21. nested = re_match.group('nested')
  22. return java_class_params_to_key(package, class_name), nested
  23. class JavaClass(graph.Node):
  24. """A representation of a Java class.
  25. Some classes may have nested classes (eg. explicitly, or
  26. implicitly through lambdas). We treat these nested classes as part of
  27. the outer class, storing only their names as metadata.
  28. """
  29. def __init__(self, package: str, class_name: str):
  30. """Initializes a new Java class structure.
  31. The package and class_name are used to create a unique key per class.
  32. Args:
  33. package: The package the class belongs to.
  34. class_name: The name of the class. For nested classes, this is
  35. the name of the class that contains them.
  36. """
  37. super().__init__(java_class_params_to_key(package, class_name))
  38. self._package = package
  39. self._class_name = class_name
  40. self._nested_classes = set()
  41. self._build_targets = set()
  42. @property
  43. def package(self):
  44. """The package the class belongs to."""
  45. return self._package
  46. @property
  47. def class_name(self):
  48. """The name of the class.
  49. For nested classes, this is the name of the class that contains them.
  50. """
  51. return self._class_name
  52. @property
  53. def nested_classes(self):
  54. """A set of nested classes contained within this class."""
  55. return self._nested_classes
  56. @nested_classes.setter
  57. def nested_classes(self, other):
  58. self._nested_classes = other
  59. @property
  60. def build_targets(self) -> Set[str]:
  61. """Which build target(s) contain the class."""
  62. # TODO(crbug.com/1124836): Make this return a List, sorted by
  63. # importance.
  64. return self._build_targets
  65. @build_targets.setter
  66. def build_targets(self, other):
  67. self._build_targets = other
  68. def add_nested_class(self, nested: str):
  69. self._nested_classes.add(nested)
  70. def add_build_target(self, build_target: str) -> None:
  71. self._build_targets.add(build_target)
  72. def get_node_metadata(self):
  73. """Generates JSON metadata for the current node.
  74. The list of nested classes is sorted in order to help with testing.
  75. Structure:
  76. {
  77. 'package': str,
  78. 'class': str,
  79. 'build_targets' [ str, ... ]
  80. 'nested_classes': [ class_key, ... ],
  81. }
  82. """
  83. return {
  84. class_json_consts.PACKAGE: self.package,
  85. class_json_consts.CLASS: self.class_name,
  86. class_json_consts.BUILD_TARGETS: sorted(self.build_targets),
  87. class_json_consts.NESTED_CLASSES: sorted(self.nested_classes),
  88. }
  89. class JavaClassDependencyGraph(graph.Graph):
  90. """A graph representation of the dependencies between Java classes.
  91. A directed edge A -> B indicates that A depends on B.
  92. """
  93. def create_node_from_key(self, key: str):
  94. """See comment above the regex definition."""
  95. re_match = JAVA_CLASS_FULL_NAME_REGEX.match(key)
  96. package = re_match.group('package')
  97. class_name = re_match.group('class_name')
  98. return JavaClass(package, class_name)