1#!/usr/bin/python3
2#
3# Copyright (C) 2015 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Common mixins and abstract base classes (ABCs) useful for writing test generators in python
19"""
20
21import abc
22import collections.abc
23import functools
24
25class Named(metaclass=abc.ABCMeta):
26  """
27  An abc that defines a get_name method.
28  """
29
30  @abc.abstractmethod
31  def get_name(self):
32    """
33    Returns a unique name to use as the identity for implementing comparisons.
34    """
35    pass
36
37class FileLike(metaclass=abc.ABCMeta):
38  """
39  An abc that defines get_file_name and get_file_extension methods.
40  """
41
42  @abc.abstractmethod
43  def get_file_name(self):
44    """Returns the filename this object represents"""
45    pass
46
47  @abc.abstractmethod
48  def get_file_extension(self):
49    """Returns the file extension of the file this object represents"""
50    pass
51
52@functools.lru_cache(maxsize=None)
53def get_file_extension_mixin(ext):
54  """
55  Gets a mixin that defines get_file_name(self) in terms of get_name(self) with the
56  given file extension.
57  """
58
59  class FExt(object):
60    """
61    A mixin defining get_file_name(self) in terms of get_name(self)
62    """
63
64    def get_file_name(self):
65      return self.get_name() + ext
66
67    def get_file_extension(self):
68      return ext
69
70  # Register the ABCs
71  Named.register(FExt)
72  FileLike.register(FExt)
73
74  return FExt
75
76class SmaliFileMixin(get_file_extension_mixin(".smali")):
77  """
78  A mixin that defines that the file this class belongs to is get_name() + ".smali".
79  """
80  pass
81
82class JavaFileMixin(get_file_extension_mixin(".java")):
83  """
84  A mixin that defines that the file this class belongs to is get_name() + ".java".
85  """
86  pass
87
88class NameComparableMixin(object):
89  """
90  A mixin that defines the object comparison and related functionality in terms
91  of a get_name(self) function.
92  """
93
94  def __lt__(self, other):
95    return self.get_name() < other.get_name()
96
97  def __gt__(self, other):
98    return self.get_name() > other.get_name()
99
100  def __eq__(self, other):
101    return self.get_name() == other.get_name()
102
103  def __le__(self, other):
104    return self.get_name() <= other.get_name()
105
106  def __ge__(self, other):
107    return self.get_name() >= other.get_name()
108
109  def __ne__(self, other):
110    return self.get_name() != other.get_name()
111
112  def __hash__(self):
113    return hash(self.get_name())
114
115Named.register(NameComparableMixin)
116collections.abc.Hashable.register(NameComparableMixin)
117
118class DumpMixin(metaclass=abc.ABCMeta):
119  """
120  A mixin to add support for dumping the string representation of an object to a
121  file. Requires the get_file_name(self) method be defined.
122  """
123
124  @abc.abstractmethod
125  def __str__(self):
126    """
127    Returns the data to be printed to a file by dump.
128    """
129    pass
130
131  def dump(self, directory):
132    """
133    Dump this object to a file in the given directory
134    """
135    out_file = directory / self.get_file_name()
136    if out_file.exists():
137      out_file.unlink()
138    with out_file.open('w') as out:
139      print(str(self), file=out)
140
141FileLike.register(DumpMixin)
142