seawolf2357 commited on
Commit
a65138c
·
verified ·
1 Parent(s): 3315103

Add tools

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. tools/README.rst +19 -0
  2. tools/constants/pyhwp_dev_constants.py +20 -0
  3. tools/constants/setup.py +4 -0
  4. tools/discover.lo/discover_lo.py +275 -0
  5. tools/discover.lo/setup.py +5 -0
  6. tools/discover.lxml/discover_lxml.py +120 -0
  7. tools/discover.lxml/setup.py +22 -0
  8. tools/discover.python/discover_python.py +222 -0
  9. tools/discover.python/setup.py +19 -0
  10. tools/download/pyhwp_download.py +71 -0
  11. tools/download/setup.py +6 -0
  12. tools/egg.path/egg_path.py +30 -0
  13. tools/egg.path/setup.py +9 -0
  14. tools/gpl/gpl/Pysec.py +432 -0
  15. tools/gpl/gpl/__init__.py +85 -0
  16. tools/gpl/gpl/parsers.py +202 -0
  17. tools/gpl/gpl/tests/__init__.py +0 -0
  18. tools/gpl/gpl/tests/test_gpl.py +171 -0
  19. tools/gpl/setup.py +9 -0
  20. tools/jingodf/jingodf/__init__.py +176 -0
  21. tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.0-os.rng +111 -0
  22. tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.1.rng +111 -0
  23. tools/jingodf/jingodf/schema/OpenDocument-schema-v1.0-os.rng +0 -0
  24. tools/jingodf/jingodf/schema/OpenDocument-schema-v1.1.rng +0 -0
  25. tools/jingodf/jingodf/schema/OpenDocument-strict-schema-v1.1.rng +61 -0
  26. tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-dsig-schema.rng +84 -0
  27. tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-manifest-schema.rng +224 -0
  28. tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-metadata.owl +86 -0
  29. tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-package-metadata.owl +81 -0
  30. tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-schema.rng +0 -0
  31. tools/jingodf/setup.py +13 -0
  32. tools/jxml/jxml.coverage/jxml_coverage.py +117 -0
  33. tools/jxml/jxml.coverage/setup.py +16 -0
  34. tools/jxml/jxml/__init__.py +0 -0
  35. tools/jxml/jxml/etree/__init__.py +1072 -0
  36. tools/jxml/lxml/lxml/__init__.py +1 -0
  37. tools/jxml/lxml/lxml/etree/__init__.py +1 -0
  38. tools/jxml/lxml/setup.py +9 -0
  39. tools/jxml/setup.py +9 -0
  40. tools/jxml/tests/Makefile +52 -0
  41. tools/jxml/tests/hello.xml +1 -0
  42. tools/jxml/tests/sample.xml +5 -0
  43. tools/jxml/tests/test_jaxp.py +69 -0
  44. tools/jxml/tests/test_lxml.py +664 -0
  45. tools/jxml/tests/text-output.xsl +7 -0
  46. tools/jxml/tests/xsl/import-test.xsl +10 -0
  47. tools/jxml/tests/xsl/imported.xsl +7 -0
  48. tools/oxt.tool/oxt_tool/__init__.py +405 -0
  49. tools/oxt.tool/oxt_tool/backend.py +361 -0
  50. tools/oxt.tool/oxt_tool/description.py +379 -0
tools/README.rst ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ``tools/`` - Development helper packages
2
+ ----------------------------------------
3
+
4
+ ``discover.python/``
5
+ ``discover.lxml/``
6
+ ``discover.jre/``
7
+ ``discover.lo/``
8
+ ``install.jython/``
9
+
10
+ Discover multiple python versions, lxml, JRE, Libreoffice to use in the
11
+ developement environment. Provides zc.buildout recipes.
12
+
13
+ ``xsltest/``
14
+
15
+ an XSLT test runner.
16
+
17
+ ``oxt.tool/``
18
+
19
+ Build and test .oxt packages with the LibreOffice.
tools/constants/pyhwp_dev_constants.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import sys
4
+
5
+
6
+ class Recipe(object):
7
+
8
+ def __init__(self, buildout, name, options):
9
+ options['pathsep'] = os.pathsep
10
+ options['sep'] = os.sep
11
+ if sys.platform == 'win32':
12
+ options['script_py_suffix'] = '-script.py'
13
+ else:
14
+ options['script_py_suffix'] = ''
15
+
16
+ def install(self):
17
+ return []
18
+
19
+ def update(self):
20
+ pass
tools/constants/setup.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from setuptools import setup
2
+ setup(name='pyhwp.dev.constants',
3
+ py_modules=['pyhwp_dev_constants'],
4
+ entry_points={'zc.buildout': ['default = pyhwp_dev_constants:Recipe']})
tools/discover.lo/discover_lo.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import os
4
+ import os.path
5
+ import sys
6
+ import logging
7
+ import contextlib
8
+ from discover_jre import executable_in_dir
9
+ from discover_jre import expose_options
10
+
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def wellknown_locations():
16
+ if sys.platform == 'win32':
17
+ program_files = 'c:\\program files'
18
+ if os.path.exists(program_files):
19
+ for name in os.listdir(program_files):
20
+ yield dict(location=os.path.join(program_files, name))
21
+ if sys.platform.startswith('linux'):
22
+ yield dict(location='/usr/lib/libreoffice',
23
+ uno_python='/usr/bin/python') # Debian/Ubuntu
24
+
25
+
26
+ def discover_lo(in_wellknown=True, in_path=True):
27
+ if in_wellknown:
28
+ for installation in discover_in_wellknown_locations():
29
+ yield installation
30
+
31
+ if in_path:
32
+ for installation in discover_in_path():
33
+ yield installation
34
+
35
+
36
+ def discover_in_wellknown_locations():
37
+ for installation in wellknown_locations():
38
+ found = contains_program(installation['location'])
39
+ if found:
40
+ if 'uno_python' not in found and 'uno_python' in installation:
41
+ uno_python = python_import_uno(installation['uno_python'])
42
+ if uno_python:
43
+ found.update(resolve_uno_components(uno_python))
44
+ installation.update(found)
45
+ installation['through'] = 'WELLKNOWN_LOCATION'
46
+ yield installation
47
+
48
+
49
+ def discover_in_path():
50
+ if 'PATH' in os.environ:
51
+ path = os.environ['PATH']
52
+ path = path.split(os.pathsep)
53
+ for dir in path:
54
+ libreoffice = contains_libreoffice(dir)
55
+ if libreoffice:
56
+ entry = dict(libreoffice=libreoffice, through='PATH')
57
+
58
+ # resolve symlinks
59
+ resolved = os.path.realpath(libreoffice)
60
+ location = os.path.dirname(os.path.dirname(resolved))
61
+ installation = contains_program(location)
62
+ if installation:
63
+ entry.update(installation)
64
+
65
+ # Debian/Ubuntu case
66
+ if 'uno' not in entry:
67
+ # try System python
68
+ uno_python = python_import_uno(sys.executable)
69
+ if uno_python:
70
+ entry.update(resolve_uno_components(uno_python))
71
+
72
+ yield entry
73
+
74
+
75
+ def contains_libreoffice(dir):
76
+ return executable_in_dir('libreoffice', dir)
77
+
78
+
79
+ def contains_program(location):
80
+ program_dir = os.path.join(location, 'program')
81
+ if os.path.isdir(program_dir):
82
+ installation = dict(location=location, program=program_dir)
83
+ soffice = executable_in_dir('soffice', program_dir)
84
+ if soffice:
85
+ installation['soffice'] = soffice
86
+ unopkg = executable_in_dir('unopkg', program_dir)
87
+ if unopkg:
88
+ installation['unopkg'] = unopkg
89
+
90
+ program_python = executable_in_dir('python', program_dir)
91
+ if program_python:
92
+ uno_python = python_import_uno(program_python)
93
+ if uno_python:
94
+ installation.update(resolve_uno_components(uno_python))
95
+
96
+ basis_link = os.path.join(location, 'basis-link')
97
+ if os.path.islink(basis_link):
98
+ location = os.path.realpath(basis_link)
99
+
100
+ ure = find_ure(location)
101
+ if ure:
102
+ installation['ure'] = ure
103
+
104
+ return installation
105
+
106
+
107
+ def find_ure(location):
108
+ ure_link = os.path.join(location, 'ure-link')
109
+ if os.path.islink(ure_link):
110
+ ure = os.path.realpath(ure_link)
111
+ if os.path.isdir(ure):
112
+ return ure
113
+
114
+ # win32
115
+ ure = os.path.join(location, 'ure')
116
+ if os.path.isdir(ure):
117
+ return ure
118
+
119
+
120
+ def python_import_uno(python):
121
+ import subprocess
122
+ cmd = [python, '-c', 'import uno, unohelper']
123
+ ret = subprocess.call(cmd)
124
+ if ret == 0:
125
+ return python
126
+
127
+
128
+ def resolve_uno_components(uno_python):
129
+ uno_python_core, modules = get_uno_locations(uno_python,
130
+ ['uno', 'pyuno',
131
+ 'unohelper'])
132
+
133
+ yield 'uno_python', uno_python
134
+ yield 'uno_python_core', uno_python_core
135
+
136
+ uno_pythonpath = set(os.path.dirname(modules[name])
137
+ for name in ['uno', 'unohelper'])
138
+ uno_pythonpath = os.pathsep.join(list(uno_pythonpath))
139
+ yield 'uno_pythonpath', uno_pythonpath
140
+
141
+ for name in modules:
142
+ yield name, modules[name]
143
+
144
+
145
+ def get_uno_locations(python, modules):
146
+ statements = ['print __import__("sys").executable']
147
+ statements.extend('print __import__("%s").__file__' % name
148
+ for name in modules)
149
+ statements = ';'.join(statements)
150
+ cmd = [python, '-c', statements]
151
+ lines = subprocess_check_output(cmd)
152
+ lines = lines.strip()
153
+ lines = lines.split('\n')
154
+ return lines[0], dict(zip(modules, lines[1:]))
155
+
156
+
157
+ def subprocess_check_output(cmd):
158
+ import tempfile
159
+ fd, name = tempfile.mkstemp()
160
+ f = os.fdopen(fd, 'r+')
161
+ try:
162
+ import subprocess
163
+ ret = subprocess.call(cmd, stdout=f)
164
+ if ret != 0:
165
+ logger.error('%d returned: %s', ret, ' '.join(cmd))
166
+ raise Exception('%s exit with %d' % (cmd[0], ret))
167
+ f.seek(0)
168
+ return f.read()
169
+ finally:
170
+ f.close()
171
+ os.unlink(name)
172
+
173
+
174
+ LO_VARS = ('libreoffice'
175
+ ' location program soffice unopkg'
176
+ ' ure'
177
+ ' uno_python uno_python_core uno_pythonpath uno pyuno unohelper').split(' ')
178
+
179
+
180
+ def log_discovered(installations):
181
+ for installation in installations:
182
+ msg = 'discovered:'
183
+ for name in LO_VARS + ['through']:
184
+ if name in installation:
185
+ msg += ' ' + name + '=' + installation[name]
186
+ logger.info(msg)
187
+ yield installation
188
+
189
+
190
+ @contextlib.contextmanager
191
+ def original_pythonpath():
192
+ ''' without buildout-modified environment variables
193
+ '''
194
+ if 'BUILDOUT_ORIGINAL_PYTHONPATH' in os.environ:
195
+ buildout_pythonpath = os.environ['PYTHONPATH']
196
+ os.environ['PYTHONPATH'] = os.environ.pop('BUILDOUT_ORIGINAL_PYTHONPATH')
197
+ yield
198
+ os.environ['BUILDOUT_ORIGINAL_PYTHONPATH'] = os.environ['PYTHONPATH']
199
+ os.environ['PYTHONPATH'] = buildout_pythonpath
200
+ else:
201
+ yield
202
+
203
+
204
+ class Discover(object):
205
+ ''' Discover a LibreOffice installation and provide its location.
206
+ '''
207
+
208
+ def __init__(self, buildout, name, options):
209
+ self.__logger = logger = logging.getLogger(name)
210
+ for k, v in options.items():
211
+ logger.info('%s: %r', k, v)
212
+
213
+ self.__recipe = options['recipe']
214
+ self.__generate_stub = None
215
+
216
+ # special marker
217
+ not_found = options.get('not-found', 'not-found')
218
+
219
+ # expose platform-specific path seperator for convinience
220
+ options['pathsep'] = os.pathsep
221
+
222
+ if 'location' in options:
223
+ # if location is explicitly specified, it must contains java
224
+ # executable.
225
+ with original_pythonpath():
226
+ discovered = contains_program(options['location'])
227
+ if discovered:
228
+ # LO found, no further operation required.
229
+ expose_options(options, LO_VARS, discovered,
230
+ not_found=not_found, logger=logger)
231
+ return
232
+ from zc.buildout import UserError
233
+ raise UserError('LO not found at %s' % options['location'])
234
+
235
+ in_wellknown = options.get('search-in-wellknown-places',
236
+ 'true').lower().strip()
237
+ in_wellknown = in_wellknown in ('true', 'yes', '1')
238
+ in_path = options.get('search-in-path', 'true').lower().strip()
239
+ in_path = in_path in ('true', 'yes', '1')
240
+
241
+ # location is not specified: try to discover a LO installation
242
+ with original_pythonpath():
243
+ discovered = discover_lo(in_wellknown, in_path)
244
+ discovered = log_discovered(discovered)
245
+ discovered = list(discovered)
246
+
247
+ if discovered:
248
+ discovered = discovered[0]
249
+ logger.info('following LO installation will be used:')
250
+ expose_options(options, LO_VARS, discovered, not_found=not_found,
251
+ logger=logger)
252
+ return
253
+
254
+ expose_options(options, LO_VARS, dict(), not_found=not_found,
255
+ logger=logger)
256
+ return
257
+
258
+ # no LO found: stub generation
259
+ parts_dir = buildout['buildout']['parts-directory']
260
+ self.__generate_stub = os.path.join(parts_dir, name)
261
+ options['location'] = self.__generate_stub
262
+ logger.info('LO not found: a dummy LO will be generated')
263
+ logger.info('location = %s (updating)', self.__generate_stub)
264
+
265
+ def install(self):
266
+ location = self.__generate_stub
267
+ if location is None:
268
+ return
269
+
270
+ if not os.path.exists(location):
271
+ os.makedirs(location)
272
+ yield location
273
+ self.__logger.info('A dummy LO has been generated: %s', location)
274
+
275
+ update = install
tools/discover.lo/setup.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from distutils.core import setup
2
+ setup(name='discover.lo',
3
+ py_modules=['discover_lo'],
4
+ entry_points={'zc.buildout': ['default = discover_lo:Discover']},
5
+ install_requires=['discover.jre'])
tools/discover.lxml/discover_lxml.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import with_statement
3
+ import logging
4
+ import os
5
+ import os.path
6
+ import sys
7
+ from discover_python import expose_options
8
+ from discover_python import log_discovered
9
+
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ EXPOSE_NAMES = ('location', 'version')
15
+
16
+
17
+ FIND_SOURCE = '''
18
+ import os
19
+ import os.path
20
+ import sys
21
+ try:
22
+ from pkg_resources import get_distribution
23
+ except ImportError:
24
+ sys.stderr.write('pkg_resources is not found' + os.linesep)
25
+ try:
26
+ import lxml
27
+ except ImportError:
28
+ sys.stderr.write('lxml is not found' + os.linesep)
29
+ raise SystemExit(1)
30
+ else:
31
+ print(os.path.dirname(lxml.__path__[0]))
32
+ print('')
33
+ raise SystemExit(0)
34
+
35
+ try:
36
+ dist = get_distribution('lxml')
37
+ except Exception:
38
+ e = sys.exc_info()[1]
39
+ sys.stderr.write(repr(e))
40
+ sys.stderr.write(os.linesep)
41
+ raise SystemExit(1)
42
+ else:
43
+ print(dist.location)
44
+ print(dist.version)
45
+ '''
46
+
47
+
48
+ def discover_lxml(executable):
49
+ import tempfile
50
+ fd, path = tempfile.mkstemp()
51
+ try:
52
+ with os.fdopen(fd, 'w') as f:
53
+ f.write(FIND_SOURCE)
54
+
55
+ from subprocess import Popen
56
+ from subprocess import PIPE
57
+ args = [executable, path]
58
+ env = dict(os.environ)
59
+ for k in ('PYTHONPATH', 'PYTHONHOME'):
60
+ if k in env:
61
+ del env[k]
62
+ try:
63
+ p = Popen(args, stdout=PIPE, env=env)
64
+ except Exception:
65
+ e = sys.exc_info()[1]
66
+ logger.exception(e)
67
+ return
68
+ else:
69
+ try:
70
+ lines = list(p.stdout)
71
+ finally:
72
+ p.wait()
73
+ finally:
74
+ os.unlink(path)
75
+
76
+ if p.returncode == 0:
77
+ location = lines[0].strip()
78
+ version = lines[1].strip()
79
+ yield dict(location=location,
80
+ version=version)
81
+
82
+
83
+ class DiscoverRecipe(object):
84
+ ''' Discover lxml and provide its location.
85
+ '''
86
+
87
+ def __init__(self, buildout, name, options):
88
+ self.__logger = logger = logging.getLogger(name)
89
+ for k, v in options.items():
90
+ logger.info('%s: %r', k, v)
91
+
92
+ self.__recipe = options['recipe']
93
+
94
+ not_found = options.get('not-found', 'not-found')
95
+ executable = options.get('python', 'python').strip()
96
+ #version = options.get('version', '').strip()
97
+
98
+ founds = discover_lxml(executable=executable)
99
+ founds = log_discovered('matching', founds, EXPOSE_NAMES,
100
+ log=logger.info)
101
+ founds = list(founds)
102
+
103
+ # location is not specified: try to discover a Python installation
104
+ if founds:
105
+ found = founds[0]
106
+ logger.info('the first-found one will be used:')
107
+ expose_options(options, EXPOSE_NAMES, found,
108
+ not_found=not_found, logger=logger)
109
+ return
110
+
111
+ # ensure executable publishes not-found marker
112
+ expose_options(options, ['location'], dict(), not_found=not_found,
113
+ logger=logger)
114
+ logger.warning('lxml not found')
115
+ return
116
+
117
+ def install(self):
118
+ return []
119
+
120
+ update = install
tools/discover.lxml/setup.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import os.path
4
+ from distutils.core import setup
5
+
6
+
7
+ def main():
8
+ cwd = os.getcwd()
9
+ setupdir = os.path.dirname(os.path.abspath(__file__))
10
+ os.chdir(setupdir)
11
+ try:
12
+ setup(name='discover.lxml',
13
+ py_modules=['discover_lxml'],
14
+ install_requires=['discover.python'],
15
+ entry_points={'zc.buildout':
16
+ ['default = discover_lxml:DiscoverRecipe']})
17
+ finally:
18
+ os.chdir(cwd)
19
+
20
+
21
+ if __name__ == '__main__':
22
+ main()
tools/discover.python/discover_python.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import logging
4
+ import os
5
+ import os.path
6
+ import sys
7
+ import re
8
+
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ EXPOSE_NAMES = ('executable', 'version', 'prefix', 'exec_prefix', 'through')
14
+
15
+
16
+ def wellknown_locations():
17
+ if sys.platform == 'win32':
18
+ base = 'c:\\'
19
+ for name in os.listdir(base):
20
+ name = name.lower()
21
+ if name.startswith('python'):
22
+ shortversion = name[len('python'):]
23
+ m = re.match('[23][0-9]', shortversion)
24
+ if m:
25
+ yield base + name
26
+ elif 'PYTHONZ_ROOT' in os.environ:
27
+ pythonz_root = os.environ['PYTHONZ_ROOT']
28
+ pythons = os.path.join(pythonz_root, 'pythons')
29
+ for item in os.listdir(pythons):
30
+ yield os.path.join(pythons, item)
31
+
32
+
33
+ def discover_python(in_wellknown=True, in_path=True):
34
+
35
+ if in_wellknown:
36
+ for found in search_in_wellknown_locations():
37
+ yield found
38
+
39
+ if in_path:
40
+ for found in search_in_path():
41
+ yield found
42
+
43
+
44
+ def search_in_wellknown_locations():
45
+ for location in wellknown_locations():
46
+ if sys.platform == 'win32':
47
+ founds = contains_python(location)
48
+ else:
49
+ founds = contains_python_in_bin(location)
50
+
51
+ for found in founds:
52
+ found['through'] = 'WELLKNOWN_LOCATION'
53
+ yield found
54
+
55
+
56
+ def search_in_path():
57
+ if 'PATH' in os.environ:
58
+ path = os.environ['PATH']
59
+ path = path.split(os.pathsep)
60
+ for dir in path:
61
+ for found in contains_python(dir):
62
+ found['through'] = 'PATH'
63
+
64
+ # resolve symlinks
65
+ resolved = os.path.realpath(found['executable'])
66
+ found['executable'] = resolved
67
+ yield found
68
+
69
+
70
+ def contains_python_in_bin(dir):
71
+ bindir = os.path.join(dir, 'bin')
72
+ return contains_python(bindir)
73
+
74
+
75
+ def contains_python(dir):
76
+ vers = {
77
+ 2: [3, 4, 5, 6, 7],
78
+ 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
79
+ }
80
+ names = (('python%d.%d' % (major, minor))
81
+ for major in reversed(sorted(vers))
82
+ for minor in reversed(sorted(vers[major])))
83
+ names = list(names) + ['python']
84
+ for name in names:
85
+ executable = executable_in_dir(name, dir)
86
+ if executable:
87
+ found = executable_is_python(executable)
88
+ if found:
89
+ yield found
90
+
91
+
92
+ def executable_in_dir(name, dir):
93
+ assert name == os.path.basename(name)
94
+ if sys.platform == 'win32':
95
+ name += '.exe'
96
+ path = os.path.join(dir, name)
97
+ if not os.path.exists(path):
98
+ return
99
+ return path
100
+
101
+
102
+ def executable_is_python(executable):
103
+ from subprocess import Popen
104
+ from subprocess import PIPE
105
+ cmd = '''
106
+ import os, sys
107
+ print(sys.hexversion)
108
+ print(os.pathsep.join([sys.prefix, sys.exec_prefix]))
109
+ '''.strip().replace('\n', ';')
110
+ args = [executable, '-c', cmd]
111
+ env = dict(os.environ)
112
+ for k in ('PYTHONPATH', 'PYTHONHOME'):
113
+ if k in env:
114
+ del env[k]
115
+ try:
116
+ p = Popen(args, stdout=PIPE, env=env)
117
+ lines = p.stdout.read().split('\n')
118
+ p.wait()
119
+ ver = int(lines[0])
120
+ ver_major = str(ver >> 24 & 0xff)
121
+ ver_minor = str(ver >> 16 & 0xff)
122
+ ver_patch = str(ver >> 8 & 0xff)
123
+ ver = ver_major, ver_minor, ver_patch
124
+ version = '.'.join(ver)
125
+ prefix, exec_prefix = lines[1].split(os.pathsep)
126
+ return dict(executable=executable, version=version,
127
+ prefix=prefix, exec_prefix=exec_prefix)
128
+ except Exception, e:
129
+ logger.error('popen failed: %s', args)
130
+ logger.exception(e)
131
+
132
+
133
+ def log_discovered(qualify, found, names, log=logger.info):
134
+ for item in found:
135
+ msg = qualify + ':'
136
+ for name in names:
137
+ if name in item:
138
+ msg += ' %s=%s' % (name, item[name])
139
+ log(msg)
140
+ yield item
141
+
142
+
143
+ def expose_options(options, names, found, not_found, logger=logger):
144
+ for name in names:
145
+ if name in found:
146
+ value = found[name]
147
+ if name in options:
148
+ if value != options[name]:
149
+ logger.info('(updating) %s = %s', name, value)
150
+ options[name] = value
151
+ else:
152
+ logger.info('(preserving) %s = %s', name, value)
153
+ else:
154
+ logger.info('(exposing) %s = %s', name, value)
155
+ options[name] = value
156
+ else:
157
+ if name not in options:
158
+ options[name] = value = not_found
159
+ logger.info('(exposing) %s = %s', name, value)
160
+
161
+
162
+ class Discover(object):
163
+ ''' Discover Python and provide its location.
164
+ '''
165
+
166
+ def __init__(self, buildout, name, options):
167
+ from zc.buildout import UserError
168
+ self.__logger = logger = logging.getLogger(name)
169
+ for k, v in options.items():
170
+ logger.info('%s: %r', k, v)
171
+
172
+ self.__recipe = options['recipe']
173
+
174
+ not_found = options.get('not-found', 'not-found')
175
+ version = options.get('version', '').strip()
176
+
177
+ if 'location' in options:
178
+ # if location is explicitly specified, it must contains java
179
+ # executable.
180
+ for found in contains_python_in_bin(options['location']):
181
+ if not version or found['version'].startswith(version):
182
+ # Python found, no further discovery required.
183
+ options['executable'] = found['executable']
184
+ return
185
+ raise UserError('Python not found at %s' % options['location'])
186
+
187
+ in_wellknown = options.get('search-in-wellknown-places',
188
+ 'true').lower().strip()
189
+ in_wellknown = in_wellknown in ('true', 'yes', '1')
190
+ in_path = options.get('search-in-path', 'true').lower().strip()
191
+ in_path = in_path in ('true', 'yes', '1')
192
+
193
+ founds = discover_python(in_wellknown=in_wellknown,
194
+ in_path=in_path)
195
+ founds = log_discovered('candidates', founds, EXPOSE_NAMES,
196
+ log=logger.debug)
197
+ if version:
198
+ # filter with version
199
+ founds = (found for found in founds
200
+ if found['version'].startswith(version))
201
+ founds = log_discovered('matching', founds, EXPOSE_NAMES,
202
+ log=logger.info)
203
+ founds = list(founds)
204
+
205
+ # location is not specified: try to discover a Python installation
206
+ if founds:
207
+ found = founds[0]
208
+ logger.info('the first-matching one will be used:')
209
+ expose_options(options, EXPOSE_NAMES, found,
210
+ not_found=not_found, logger=logger)
211
+ return
212
+
213
+ # ensure executable publishes not-found marker
214
+ expose_options(options, ['executable'], dict(), not_found=not_found,
215
+ logger=logger)
216
+ logger.warning('Python not found')
217
+ return
218
+
219
+ def install(self):
220
+ return []
221
+
222
+ update = install
tools/discover.python/setup.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import os.path
4
+ from distutils.core import setup
5
+
6
+ def main():
7
+ cwd = os.getcwd()
8
+ setupdir = os.path.dirname(os.path.abspath(__file__))
9
+ os.chdir(setupdir)
10
+ try:
11
+ setup(name='discover.python',
12
+ py_modules=['discover_python'],
13
+ entry_points={'zc.buildout': ['default = discover_python:Discover']})
14
+ finally:
15
+ os.chdir(cwd)
16
+
17
+
18
+ if __name__ == '__main__':
19
+ main()
tools/download/pyhwp_download.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import sys
3
+ import urlparse
4
+ import os.path
5
+ import logging
6
+ from binascii import a2b_hex
7
+ from binascii import b2a_hex
8
+ from hashlib import md5
9
+
10
+ import requests
11
+
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def main():
17
+ logging.basicConfig()
18
+ logger.setLevel(logging.DEBUG)
19
+
20
+ url = sys.argv[1]
21
+ dst = sys.argv[2]
22
+ md5_given = a2b_hex(sys.argv[3])
23
+ result = urlparse.urlparse(url)
24
+
25
+ filename = os.path.basename(result.path)
26
+
27
+ if not os.path.exists(dst):
28
+ destination_path = dst
29
+ logger.debug('%s not exists: destination=%s', dst, destination_path)
30
+ else:
31
+ if os.path.isdir(dst):
32
+ destination_path = os.path.join(dst, filename)
33
+ logger.debug('%s is a directory: destination=%s', dst,
34
+ destination_path)
35
+ else:
36
+ destination_path = dst
37
+
38
+ if os.path.exists(destination_path):
39
+ md5_existing = md5_file(destination_path)
40
+ if md5_given == md5_existing:
41
+ logger.debug('%s exists: skipped', destination_path)
42
+ return
43
+
44
+ response = requests.get(url, stream=True)
45
+ response.raise_for_status()
46
+ with open(destination_path, 'wb') as f:
47
+ copy_stream(response.raw, f)
48
+
49
+ md5_downloaded = md5_file(destination_path)
50
+ if md5_given != md5_downloaded:
51
+ logger.error('md5 not match: %s', b2a_hex(md5_downloaded))
52
+ raise SystemExit(1)
53
+
54
+
55
+ def copy_stream(src, dst):
56
+ while True:
57
+ data = src.read(16384)
58
+ if len(data) == 0:
59
+ break
60
+ dst.write(data)
61
+
62
+
63
+ def md5_file(path):
64
+ with open(path, 'rb') as f:
65
+ m = md5('')
66
+ while True:
67
+ data = f.read(16384)
68
+ if len(data) == 0:
69
+ break
70
+ m.update(data)
71
+ return m.digest()
tools/download/setup.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from setuptools import setup
3
+ setup(name='pyhwp.develop.download',
4
+ py_modules=['pyhwp_download'],
5
+ install_requires=['requests'],
6
+ entry_points=dict(console_scripts=['pyhwp-download = pyhwp_download:main']))
tools/egg.path/egg_path.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import logging
3
+
4
+
5
+ class EggPath(object):
6
+ def __init__(self, buildout, name, options):
7
+ self.__name = name
8
+ self.__logger = logging.getLogger(name)
9
+
10
+ eggs = options['eggs']
11
+ eggs = eggs.split('\n')
12
+ eggs = list(egg.strip() for egg in eggs)
13
+ for egg in eggs:
14
+ self.__logger.info('egg: %s', egg)
15
+
16
+ from zc.recipe.egg.egg import Eggs
17
+ eggs_recipe = Eggs(buildout, name, options)
18
+ req, ws = eggs_recipe.working_set()
19
+ for dist in ws:
20
+ self.__logger.debug('dist: %s %s at %s', dist, dist.key, dist.location)
21
+ dist_locations = dict((dist.key, dist.location) for dist in ws)
22
+ egg_path = list(dist_locations[egg] for egg in eggs)
23
+ for p in egg_path:
24
+ self.__logger.info('egg-path: %s', p)
25
+ options['egg-path'] = ' '.join(egg_path)
26
+
27
+ def install(self):
28
+ return []
29
+
30
+ update = install
tools/egg.path/setup.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup
2
+ setup(name='egg.path',
3
+ install_requires=['zc.recipe.egg'],
4
+ py_modules=['egg_path'],
5
+ entry_points = {
6
+ 'zc.buildout': [
7
+ 'default = egg_path:EggPath'
8
+ ]
9
+ })
tools/gpl/gpl/Pysec.py ADDED
@@ -0,0 +1,432 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # originally authored by Peter Thatcher, Public Domain
3
+ # See http://www.valuedlessons.com/2008/02/pysec-monadic-combinatoric-parsing-in.html
4
+
5
+ def Record(*props):
6
+ class cls(RecordBase):
7
+ pass
8
+
9
+ cls.setProps(props)
10
+
11
+ return cls
12
+
13
+
14
+ class RecordBase(tuple):
15
+ PROPS = ()
16
+
17
+ def __new__(cls, *values):
18
+ if cls.prepare != RecordBase.prepare:
19
+ values = cls.prepare(*values)
20
+ return cls.fromValues(values)
21
+
22
+ @classmethod
23
+ def fromValues(cls, values):
24
+ return tuple.__new__(cls, values)
25
+
26
+ def __repr__(self):
27
+ return self.__class__.__name__ + tuple.__repr__(self)
28
+
29
+ ## overridable
30
+ @classmethod
31
+ def prepare(cls, *args):
32
+ return args
33
+
34
+ ## setting up getters and setters
35
+ @classmethod
36
+ def setProps(cls, props):
37
+ for index, prop in enumerate(props):
38
+ cls.setProp(index, prop)
39
+ cls.PROPS = props
40
+
41
+ @classmethod
42
+ def setProp(cls, index, prop):
43
+ getter_name = prop
44
+ setter_name = "set" + prop[0].upper() + prop[1:]
45
+
46
+ setattr(cls, getter_name, cls.makeGetter(index, prop))
47
+ setattr(cls, setter_name, cls.makeSetter(index, prop))
48
+
49
+ @classmethod
50
+ def makeGetter(cls, index, prop):
51
+ return property(fget = lambda self : self[index])
52
+
53
+ @classmethod
54
+ def makeSetter(cls, index, prop):
55
+ def setter(self, value):
56
+ values = (value if current_index == index
57
+ else current_value
58
+ for current_index, current_value
59
+ in enumerate(self))
60
+ return self.fromValues(values)
61
+ return setter
62
+
63
+
64
+ class ByteStream(Record("bytes", "index")):
65
+ @classmethod
66
+ def prepare(cls, bytes, index = 0):
67
+ return (bytes, index)
68
+
69
+ def get(self, count):
70
+ start = self.index
71
+ end = start + count
72
+ bytes = self.bytes[start : end]
73
+ return bytes, (self.setIndex(end) if bytes else self)
74
+
75
+
76
+ def make_decorator(func, *dec_args):
77
+ def decorator(undecorated):
78
+ def decorated(*args, **kargs):
79
+ return func(undecorated, args, kargs, *dec_args)
80
+
81
+ decorated.__name__ = undecorated.__name__
82
+ return decorated
83
+
84
+ decorator.__name__ = func.__name__
85
+ return decorator
86
+
87
+
88
+ decorator = make_decorator
89
+
90
+
91
+ class Monad:
92
+ ## Must be overridden
93
+ def bind(self, func):
94
+ raise NotImplementedError
95
+
96
+ @classmethod
97
+ def unit(cls, val):
98
+ raise NotImplementedError
99
+
100
+ @classmethod
101
+ def lift(cls, func):
102
+ return (lambda val : cls.unit(func(val)))
103
+
104
+ ## useful defaults that should probably NOT be overridden
105
+ def __rshift__(self, bindee):
106
+ return self.bind(bindee)
107
+
108
+ def __and__(self, monad):
109
+ return self.shove(monad)
110
+
111
+ ## could be overridden if useful or if more efficient
112
+ def shove(self, monad):
113
+ return self.bind(lambda _ : monad)
114
+
115
+
116
+ class StateChanger(Record("changer", "bindees"), Monad):
117
+ @classmethod
118
+ def prepare(cls, changer, bindees = ()):
119
+ return (changer, bindees)
120
+
121
+ # binding can be slow since it happens at bind time rather than at run time
122
+ def bind(self, bindee):
123
+ return self.setBindees(self.bindees + (bindee,))
124
+
125
+ def __call__(self, state):
126
+ return self.run(state)
127
+
128
+ def run(self, state0):
129
+ value, state = self.changer(state0) if callable(self.changer) else self.changer
130
+ state = state0 if state is None else state
131
+
132
+ for bindee in self.bindees:
133
+ value, state = bindee(value).run(state)
134
+ return (value, state)
135
+
136
+ @classmethod
137
+ def unit(cls, value):
138
+ return cls((value, None))
139
+
140
+
141
+ ######## Parser Monad ###########
142
+
143
+
144
+ class ParserState(Record("stream", "position")):
145
+ @classmethod
146
+ def prepare(cls, stream, position = 0):
147
+ return (stream, position)
148
+
149
+ def read(self, count):
150
+ collection, stream = self.stream.get(count)
151
+ return collection, self.fromValues((stream, self.position + count))
152
+
153
+
154
+ class Parser(StateChanger):
155
+ def parseString(self, bytes):
156
+ return self.parseStream(ByteStream(bytes))
157
+
158
+ def parseStream(self, stream):
159
+ state = ParserState(stream)
160
+ value, state = self.run(state)
161
+ return value
162
+
163
+
164
+ class ParseFailed(Exception):
165
+ def __init__(self, message, state):
166
+ self.message = message
167
+ self.state = state
168
+ Exception.__init__(self, message)
169
+
170
+
171
+ @decorator
172
+ def parser(func, func_args, func_kargs):
173
+ def changer(state):
174
+ return func(state, *func_args, **func_kargs)
175
+ changer.__name__ = func.__name__
176
+ return Parser(changer)
177
+
178
+
179
+ ##### combinatoric functions #########
180
+
181
+
182
+ @parser
183
+ def tokens(state0, count, process):
184
+ tokens, state1 = state0.read(count)
185
+
186
+ passed, value = process(tokens)
187
+ if passed:
188
+ return (value, state1)
189
+ else:
190
+ raise ParseFailed(value, state0)
191
+
192
+
193
+ def read(count):
194
+ return tokens(count, lambda values : (True, values))
195
+
196
+
197
+ @parser
198
+ def skip(state0, parser):
199
+ value, state1 = parser(state0)
200
+ return (None, state1)
201
+
202
+
203
+ @parser
204
+ def option(state, default_value, parser):
205
+ try:
206
+ return parser(state)
207
+ except ParseFailed, failure:
208
+ if failure.state == state:
209
+ return (default_value, state)
210
+ else:
211
+ raise
212
+
213
+
214
+ @parser
215
+ def choice(state, parsers):
216
+ for parser in parsers:
217
+ try:
218
+ return parser(state)
219
+ except ParseFailed, failure:
220
+ if failure.state != state:
221
+ raise failure
222
+ raise ParseFailed("no choices were found", state)
223
+
224
+
225
+ @parser
226
+ def match(state0, expected):
227
+ actual, state1 = read(len(expected))(state0)
228
+ if actual == expected:
229
+ return actual, state1
230
+ else:
231
+ raise ParseFailed("expected %r, actual %r" % (expected, actual), state0)
232
+
233
+
234
+ def between(before, inner, after):
235
+ return before & inner >> (lambda value : after & Parser.unit(value))
236
+
237
+
238
+ def quoted(before, inner, after):
239
+ return between(match(before), inner, match(after))
240
+
241
+
242
+ def quoted_collection(start, space, inner, joiner, end):
243
+ return quoted(start, space & sep_end_by(inner, joiner), end)
244
+
245
+
246
+ @parser
247
+ def many(state, parser, min_count = 0):
248
+ values = []
249
+
250
+ try:
251
+ while True:
252
+ value, state = parser(state)
253
+ values.append(value)
254
+ except ParseFailed:
255
+ if len(values) < min_count:
256
+ raise
257
+
258
+ return values, state
259
+
260
+
261
+ @parser
262
+ def group(state, parsers):
263
+ values = []
264
+
265
+ for parser in parsers:
266
+ value, state = parser(state)
267
+ values.append(value)
268
+
269
+ return values, state
270
+
271
+
272
+ def pair(parser1, parser2):
273
+ # return group((parser1, parser2))
274
+ return parser1 >> (lambda value1 : parser2 >> (lambda value2 : Parser.unit((value1, value2))))
275
+
276
+
277
+ @parser
278
+ def skip_many(state, parser):
279
+ try:
280
+ while True:
281
+ value, state = parser(state)
282
+ except ParseFailed:
283
+ return (None, state)
284
+
285
+
286
+ def skip_before(before, parser):
287
+ return skip(before) & parser
288
+
289
+
290
+ @parser
291
+ def skip_after(state0, parser, after):
292
+ value, state1 = parser(state0)
293
+ _, state2 = after(state1)
294
+ return value, state2
295
+
296
+
297
+ @parser
298
+ def option_many(state0, first, repeated, min_count = 0):
299
+ try:
300
+ first_value, state1 = first(state0)
301
+ except ParseFailed:
302
+ if min_count > 0:
303
+ raise
304
+ else:
305
+ return [], state0
306
+ else:
307
+ values, state2 = many(repeated, min_count-1)(state1)
308
+ values.insert(0, first_value)
309
+ return values, state2
310
+
311
+
312
+ # parser separated and ended by sep
313
+ def end_by(parser, sep_parser, min_count = 0):
314
+ return many(skip_after(parser, sep_parser), min_count)
315
+
316
+
317
+ # parser separated by sep
318
+ def sep_by(parser, sep_parser, min_count = 0):
319
+ return option_many(parser, skip_before(sep_parser, parser), min_count)
320
+
321
+
322
+ # parser separated and optionally ended by sep
323
+ def sep_end_by(parser, sep_parser, min_count = 0):
324
+ return skip_after(sep_by(parser, sep_parser, min_count), option(None, sep_parser))
325
+
326
+
327
+ ##### char-specific parsing ###########
328
+
329
+
330
+ def satisfy(name, passes):
331
+ return tokens(1, lambda char : (True, char) if passes(char) else (False, "not " + name))
332
+
333
+
334
+ def one_of(chars):
335
+ char_set = frozenset(chars)
336
+ return satisfy("one of %r" % chars, lambda char : char in char_set)
337
+
338
+
339
+ def none_of(chars):
340
+ char_set = frozenset(chars)
341
+ return satisfy("not one of %r" % chars, lambda char : char and char not in char_set)
342
+
343
+
344
+ def maybe_match_parser(parser):
345
+ return match(parser) if isinstance(parser, str) else parser
346
+
347
+
348
+ def maybe_match_parsers(parsers):
349
+ return tuple(maybe_match_parser(parser) for parser in parsers)
350
+
351
+
352
+ def many_chars(parser, min_count = 0):
353
+ return join_chars(many(parser, min_count))
354
+
355
+
356
+ def option_chars(parsers):
357
+ return option("", group_chars(parsers))
358
+
359
+
360
+ def group_chars(parsers):
361
+ return join_chars(group(maybe_match_parsers(parsers)))
362
+ #return join_chars(group(parsers))
363
+
364
+
365
+ def join_chars(parser):
366
+ return parser >> Parser.lift("".join)
367
+
368
+
369
+ def while_one_of(chars, min_count = 0):
370
+ return many_chars(one_of(chars), min_count)
371
+
372
+
373
+ def until_one_of(chars, min_count = 0):
374
+ return many_chars(none_of(chars), min_count)
375
+
376
+
377
+ def char_range(begin, end):
378
+ return "".join(chr(num) for num in xrange(ord(begin), ord(end)))
379
+
380
+
381
+ def quoted_chars(start, end):
382
+ assert len(end) == 1, "end string must be exactly 1 character"
383
+ return quoted(start, many_chars(none_of(end)), end)
384
+
385
+
386
+ digit = one_of(char_range("0", "9"))
387
+ digits = many_chars(digit, min_count = 1)
388
+ space = one_of(" \v\f\t\r\n")
389
+ spaces = skip_many(space)
390
+
391
+
392
+ ############# simplified JSON ########################
393
+
394
+ #from Pysec import Parser, choice, quoted_chars, group_chars, option_chars, digits, between, pair, spaces, match, quoted_collection, sep_end_by
395
+
396
+ #HACK: json_choices is used to get around mutual recursion
397
+ #a json is value is one of text, number, mapping, and collection, which we define later
398
+ json_choices = []
399
+ json = choice(json_choices)
400
+
401
+ #text is any characters between quotes
402
+ text = quoted_chars("'", "'")
403
+
404
+ #sort of like the regular expression -?[0-9]+(\.[0-9]+)?
405
+ #in case you're unfamiliar with monads, "parser >> Parser.lift(func)" means "pass the parsed value into func but give me a new Parser back"
406
+ number = group_chars([option_chars(["-"]), digits, option_chars([".", digits])]) >> Parser.lift(float)
407
+
408
+ #quoted_collection(start, space, inner, joiner, end) means "a list of inner separated by joiner surrounded by start and end"
409
+ #also, we have to put a lot of spaces in there since JSON allows lot of optional whitespace
410
+ joiner = between(spaces, match(","), spaces)
411
+ mapping_pair = pair(text, spaces & match(":") & spaces & json)
412
+ collection = quoted_collection("[", spaces, json, joiner, "]") >> Parser.lift(list)
413
+ mapping = quoted_collection("{", spaces, mapping_pair, joiner, "}") >> Parser.lift(dict)
414
+
415
+ #HACK: finish the work around mutual recursion
416
+ json_choices.extend([text, number, mapping, collection])
417
+
418
+
419
+ ############# simplified CSV ########################
420
+
421
+ def line(cell):
422
+ return sep_end_by(cell, match(","))
423
+
424
+ def csv(cell):
425
+ return sep_end_by(line(cell), match("\n"))
426
+
427
+ ############# testing ####################
428
+
429
+ if __name__ == '__main__':
430
+ print json.parseString("{'a' : -1.0, 'b' : 2.0, 'z' : {'c' : [1.0, [2.0, [3.0]]]}}")
431
+ print csv(number).parseString("1,2,3\n4,5,6")
432
+ print csv(json).parseString("{'a' : 'A'},[1, 2, 3],'zzz'\n-1.0,2.0,-3.0")
tools/gpl/gpl/__init__.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ '''
3
+ Usage:
4
+ gpl [options] <files>...
5
+ gpl -h | --help
6
+ gpl --version
7
+
8
+ Options:
9
+ -h --help Show this screen
10
+ --version Show version
11
+ --add-year=<year> Add release year
12
+ --set-author=<author> Set author
13
+ '''
14
+ import logging
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def main():
21
+ from docopt import docopt
22
+ args = docopt(__doc__, version='0.0.0')
23
+
24
+ logger.setLevel(logging.INFO)
25
+ logger.addHandler(logging.StreamHandler())
26
+
27
+ license_modifiers = []
28
+
29
+ add_year = args['--add-year']
30
+ if add_year:
31
+ year = add_year.strip()
32
+ year = int(year)
33
+ def add_year(license):
34
+ years = license.copyright.years
35
+ years.add(year)
36
+ copyright = license.copyright.setYears(years)
37
+ return license.setCopyright(copyright)
38
+ license_modifiers.append(add_year)
39
+
40
+ set_author = args['--set-author']
41
+ if set_author:
42
+ author = set_author.strip()
43
+ from parsers import AUTHOR
44
+ author = AUTHOR.parseString(author)
45
+ def set_author(license):
46
+ copyright = license.copyright
47
+ copyright = copyright.setAuthors([author])
48
+ return license.setCopyright(copyright)
49
+ license_modifiers.append(set_author)
50
+
51
+
52
+ filenames = args['<files>']
53
+
54
+ for path in filenames:
55
+ logger.info('filename: %s', path)
56
+ try:
57
+ process_file(path, license_modifiers)
58
+ except Exception, e:
59
+ logger.exception(e)
60
+
61
+
62
+ def process_file(path, license_modifiers):
63
+ import os
64
+ import tempfile
65
+ import shutil
66
+ from parsers import parse_file
67
+
68
+ license = parse_file(path)
69
+ for modifier in license_modifiers:
70
+ license = modifier(license)
71
+
72
+ fd, tmp_path = tempfile.mkstemp()
73
+ try:
74
+ with os.fdopen(fd, 'w') as f:
75
+ f.write(str(license))
76
+ except:
77
+ os.unlink(tmp_path)
78
+ raise
79
+ else:
80
+ shutil.move(tmp_path, path)
81
+
82
+
83
+ def stringify_years(years):
84
+ from parsers import Span
85
+ return ','.join(str(span) for span in Span.from_set(years))
tools/gpl/gpl/parsers.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ from gpl.Pysec import Record
5
+ from gpl.Pysec import one_of
6
+ from gpl.Pysec import none_of
7
+ from gpl.Pysec import between
8
+ from gpl.Pysec import digits
9
+ from gpl.Pysec import match
10
+ from gpl.Pysec import sep_by
11
+ from gpl.Pysec import pair
12
+ from gpl.Pysec import Parser
13
+ from gpl.Pysec import until_one_of
14
+ from gpl.Pysec import option
15
+ from gpl.Pysec import space
16
+ from gpl.Pysec import spaces
17
+ from gpl.Pysec import quoted
18
+ #from gpl.Pysec import quoted_chars
19
+ from gpl.Pysec import char_range
20
+ from gpl.Pysec import many_chars
21
+ from gpl.Pysec import group_chars
22
+ from gpl.Pysec import skip_before
23
+ from gpl.Pysec import skip_after
24
+ from gpl.Pysec import skip_many
25
+ from gpl.Pysec import many
26
+ from gpl.Pysec import group
27
+ from gpl.Pysec import parser
28
+ from gpl.Pysec import ParseFailed
29
+
30
+ lift = Parser.lift
31
+
32
+ inline_space = one_of(" \v\f\t\r")
33
+ inline_spaces = skip_many(inline_space)
34
+ meaningful_spaces = many_chars(space)
35
+
36
+ def quoted_chars_inline(start, end):
37
+ return quoted(start, many_chars(none_of(end+'\n')), end)
38
+
39
+
40
+ def until_one_of_inline(chars):
41
+ return until_one_of(chars+'\n')
42
+
43
+
44
+ def skip_tailspace_of_line(parser):
45
+ return skip_after(parser,
46
+ inline_spaces & option(None, match('\n')))
47
+
48
+
49
+ @parser
50
+ def until_not_but(state0, should_not, but):
51
+ state = state0
52
+ values = []
53
+ while True:
54
+ try:
55
+ should_not_value, next_state = should_not(state)
56
+ return values, state
57
+ except ParseFailed:
58
+ value, state = but(state)
59
+ values.append(value)
60
+
61
+
62
+ def py_comment(parser):
63
+ return inline_spaces & match('#') & parser
64
+
65
+
66
+ class Project(Record('name', 'description')):
67
+
68
+ @classmethod
69
+ def prepare(cls, name, description=None):
70
+ name = name.strip()
71
+ if description is not None:
72
+ description = description.strip()
73
+ return name, description
74
+
75
+ def __str__(self):
76
+ return self.name + (' : ' + self.description
77
+ if self.description
78
+ else '')
79
+
80
+
81
+ alphabet = char_range('a', 'z') + char_range('A', 'Z')
82
+ PROJECT_NAME = (group_chars([one_of(alphabet),
83
+ many_chars(one_of(alphabet + char_range('0', '9') + '-_'), 1)])
84
+ >> lift(str.strip))
85
+ PROJECT_NAME = skip_after(PROJECT_NAME, option(None, inline_spaces))
86
+ PROJECT_DESC = until_one_of('\n') >> lift(str.strip)
87
+ PROJECT_LINE = pair(PROJECT_NAME,
88
+ option(None, match(':') & PROJECT_DESC))
89
+ PROJECT_LINE = PROJECT_LINE >> lift(lambda seq: Project(*seq))
90
+ PROJECT_LINE = skip_before(inline_spaces, PROJECT_LINE)
91
+ PROJECT_LINE = skip_tailspace_of_line(PROJECT_LINE)
92
+
93
+
94
+ COPYRIGHT_SIGN = match('Copyright (C)')
95
+
96
+ class Span(Record('start', 'end')):
97
+
98
+ @classmethod
99
+ def prepare(cls, start, end=None):
100
+ if end is None:
101
+ end = start
102
+ assert start <= end
103
+ return start, end
104
+
105
+ def as_set(self):
106
+ return set(range(self.start, self.end + 1))
107
+
108
+ @classmethod
109
+ def from_set(cls, valueset):
110
+ span = None
111
+ for value in sorted(valueset):
112
+ if span is None:
113
+ # at first
114
+ span = Span(value, value)
115
+ elif value == span.end + 1:
116
+ # continue current span
117
+ span = span.setEnd(value)
118
+ else:
119
+ # end current and start next span
120
+ yield span
121
+ span = Span(value, value)
122
+ if span is not None:
123
+ yield span
124
+
125
+ def __str__(self):
126
+ if self.start == self.end:
127
+ return str(self.start)
128
+ return '%d-%d' % self
129
+
130
+
131
+ YEAR = digits >> lift(int)
132
+ TAIL = option(None, match('-') & YEAR)
133
+ YEAR_SPAN = (pair(YEAR, TAIL) >> lift(lambda pair: Span(*pair)))
134
+ YEARS = (sep_by(YEAR_SPAN, match(','))
135
+ >> lift(lambda spans: reduce(set.union,
136
+ (span.as_set() for span in spans))))
137
+
138
+ class Author(Record('name', 'email')):
139
+
140
+ @classmethod
141
+ def prepare(self, name, email=None):
142
+ if not name and not email:
143
+ raise ValueError('either of name and email should not be empty')
144
+ return (name.strip() if name else None,
145
+ email.strip() if email else None)
146
+
147
+ def __str__(self):
148
+ name = self.name or ''
149
+ email = ('<' + self.email + '>') if self.email else ''
150
+ if not email:
151
+ return name
152
+ if not name:
153
+ return email
154
+ return name + ' ' + email
155
+
156
+
157
+ AUTHOR_NAME = until_one_of('<,\n') >> lift(str.strip)
158
+ AUTHOR_EMAIL = quoted_chars_inline('<', '>')
159
+ AUTHOR = (pair(option(None, AUTHOR_NAME), option(None, AUTHOR_EMAIL))
160
+ >> lift(lambda author: Author(*author)))
161
+ joiner = between(spaces, match(","), spaces)
162
+ AUTHORS = sep_by(AUTHOR, joiner)
163
+
164
+
165
+ class Copyright(Record('years', 'authors')):
166
+ def __str__(self):
167
+ years = ','.join(str(span) for span in Span.from_set(self.years))
168
+ authors = ', '.join(str(author) for author in self.authors)
169
+ return 'Copyright (C) %s %s' % (years, authors)
170
+
171
+
172
+ COPYRIGHT_LINE = (COPYRIGHT_SIGN & inline_spaces &
173
+ pair(YEARS, inline_spaces & AUTHORS))
174
+ COPYRIGHT_LINE = skip_before(inline_spaces, COPYRIGHT_LINE)
175
+ COPYRIGHT_LINE = skip_tailspace_of_line(COPYRIGHT_LINE)
176
+ COPYRIGHT_LINE = COPYRIGHT_LINE >> lift(lambda seq: Copyright(*seq))
177
+
178
+ GENERIC_LINE = skip_after(many_chars(none_of('\n')), match('\n'))
179
+
180
+
181
+ class License(Record('prolog', 'project', 'copyright', 'epilog')):
182
+
183
+ def __str__(self):
184
+ return '\n'.join(self.prolog +
185
+ ['# ' + str(self.project),
186
+ '# ' + str(self.copyright)] +
187
+ self.epilog + [''])
188
+
189
+
190
+ PROLOG = until_not_but(py_comment(PROJECT_LINE), GENERIC_LINE)
191
+ EPILOG = many(GENERIC_LINE)
192
+ LICENSE = (group([PROLOG,
193
+ py_comment(PROJECT_LINE),
194
+ py_comment(COPYRIGHT_LINE),
195
+ EPILOG])
196
+ >> lift(lambda seq: License(*seq)))
197
+
198
+
199
+ def parse_file(path):
200
+ with file(path) as f:
201
+ text = f.read()
202
+ return LICENSE.parseString(text)
tools/gpl/gpl/tests/__init__.py ADDED
File without changes
tools/gpl/gpl/tests/test_gpl.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from unittest import TestCase
3
+
4
+
5
+ class SpanTest(TestCase):
6
+
7
+ def test_from_set(self):
8
+ from gpl.parsers import Span
9
+ self.assertEqual([Span(1)],
10
+ list(Span.from_set([1])))
11
+ self.assertEqual([Span(1, 2)],
12
+ list(Span.from_set([1, 2])))
13
+ self.assertEqual([Span(1, 2), Span(4)],
14
+ list(Span.from_set([1, 2, 4])))
15
+ self.assertEqual([Span(1, 2), Span(4, 6)],
16
+ list(Span.from_set([1, 2, 4, 5, 6])))
17
+
18
+ def test_str(self):
19
+ from gpl.parsers import Span
20
+ self.assertEqual('3', str(Span(3)))
21
+ self.assertEqual('3-4', str(Span(3, 4)))
22
+ self.assertEqual('3-6', str(Span(3, 6)))
23
+
24
+
25
+ project_line = ' pyhwp : hwp file format parser in python'
26
+ copyright_line = ' Copyright (C) 2010-2012 mete0r '
27
+ generic_line = ' abc '
28
+ LF = '\n'
29
+
30
+
31
+ class ProjectTest(TestCase):
32
+
33
+ def test_project_name(self):
34
+ from gpl.parsers import PROJECT_NAME
35
+ self.assertEqual('pyhwp', PROJECT_NAME.parseString('pyhwp :'))
36
+
37
+ def test_project_desc(self):
38
+ from gpl.parsers import PROJECT_DESC
39
+ self.assertEqual('hwp file format parser in python',
40
+ PROJECT_DESC.parseString(' hwp file format parser in python '))
41
+
42
+ def test_project_line_with_lf(self):
43
+ from gpl.parsers import Project
44
+ from gpl.parsers import PROJECT_LINE
45
+
46
+ # ok with LF
47
+ self.assertEqual(Project('pyhwp', 'hwp file format parser in python'),
48
+ PROJECT_LINE.parseString(project_line + LF))
49
+ self.assertEqual(Project('pyhwp'),
50
+ PROJECT_LINE.parseString(' pyhwp ' + LF))
51
+
52
+ def test_project_line_without_lf(self):
53
+ from gpl.parsers import Project
54
+ from gpl.parsers import PROJECT_LINE
55
+
56
+ # ok without LF
57
+ self.assertEqual(Project('pyhwp', 'hwp file format parser in python'),
58
+ PROJECT_LINE.parseString(project_line))
59
+ self.assertEqual(Project('pyhwp'),
60
+ PROJECT_LINE.parseString(' pyhwp '))
61
+
62
+ def test_project_line_parser_doesnt_consume_after_lf(self):
63
+ from gpl.parsers import PROJECT_LINE
64
+ # make sure that the parser does not consume after LF
65
+ from gpl.Pysec import match
66
+ self.assertEqual(' NEXTLINE',
67
+ (PROJECT_LINE & match(' NEXTLINE')).parseString(project_line
68
+ + LF + ' NEXTLINE'))
69
+
70
+
71
+ class CopyrightTest(TestCase):
72
+
73
+ def test_stringify_years(self):
74
+ from gpl import stringify_years
75
+ self.assertEqual('2011-2012',
76
+ stringify_years([2011, 2012]))
77
+ self.assertEqual('2011-2013',
78
+ stringify_years([2011, 2012, 2013]))
79
+ self.assertEqual('2011-2013,2015',
80
+ stringify_years([2011, 2012, 2013, 2015]))
81
+ self.assertEqual('2009,2011-2013,2015',
82
+ stringify_years([2009, 2011, 2012, 2013, 2015]))
83
+
84
+ def test_copyright(self):
85
+ from gpl.parsers import COPYRIGHT_SIGN
86
+ self.assertTrue(COPYRIGHT_SIGN.parseString('Copyright (C)'))
87
+
88
+ from gpl.parsers import Span
89
+ self.assertEqual('2010', str(Span(2010)))
90
+ self.assertEqual('2010-2012', str(Span(2010, 2012)))
91
+
92
+ from gpl.parsers import YEAR_SPAN
93
+ self.assertEqual(Span(2010, 2012),
94
+ YEAR_SPAN.parseString('2010-2012'))
95
+ self.assertEqual(Span(2010, 2010),
96
+ YEAR_SPAN.parseString('2010'))
97
+
98
+ from gpl.parsers import YEARS
99
+ self.assertEqual(set([2010]),
100
+ YEARS.parseString('2010'))
101
+ self.assertEqual(set([2010, 2011]),
102
+ YEARS.parseString('2010,2011'))
103
+ self.assertEqual(set([2010, 2011, 2012]),
104
+ YEARS.parseString('2010-2012'))
105
+ self.assertEqual(set([2010, 2011, 2013, 2014, 2015, 2017]),
106
+ YEARS.parseString('2010,2011,2013-2015,2017'))
107
+
108
+ from gpl.parsers import AUTHOR_NAME
109
+ self.assertEqual('Hello World',
110
+ AUTHOR_NAME.parseString('Hello World'))
111
+ self.assertEqual('Hello World',
112
+ AUTHOR_NAME.parseString('Hello World <'))
113
+
114
+ from gpl.parsers import AUTHOR_EMAIL
115
+ self.assertEqual('user@example.tld',
116
+ AUTHOR_EMAIL.parseString('<user@example.tld>'))
117
+
118
+ from gpl.parsers import Author
119
+ from gpl.parsers import AUTHOR
120
+ self.assertEqual(Author('hong gil-dong', 'hongd@example.tld'),
121
+ AUTHOR.parseString('hong gil-dong <hongd@example.tld>'))
122
+ self.assertEqual(Author('hong gil-dong'),
123
+ (AUTHOR.parseString('hong gil-dong')))
124
+ self.assertEqual(Author(None, 'hongd@example.tld'),
125
+ (AUTHOR.parseString('<hongd@example.tld>')))
126
+
127
+ from gpl.parsers import AUTHORS
128
+ self.assertEqual([Author('mete0r'),
129
+ Author('hong gil-dong', 'hongd@ex.tld')],
130
+ AUTHORS.parseString('mete0r, hong gil-dong <hongd@ex.tld>'))
131
+
132
+ from gpl.parsers import Copyright
133
+ from gpl.parsers import COPYRIGHT_LINE
134
+ # ok with LF
135
+ self.assertEqual(Copyright(set([2010, 2011, 2012]),
136
+ [Author('mete0r')]),
137
+ (COPYRIGHT_LINE.parseString(copyright_line + LF)))
138
+
139
+ # ok without LF
140
+ self.assertEqual(Copyright(set([2010, 2011, 2012]),
141
+ [Author('mete0r')]),
142
+ (COPYRIGHT_LINE.parseString(copyright_line)))
143
+
144
+ # make sure that the parser does not consume after the LF
145
+ from gpl.Pysec import match
146
+ self.assertEqual(' NEXTLINE',
147
+ (COPYRIGHT_LINE & match(' NEXTLINE')).parseString(copyright_line + LF + ' NEXTLINE'))
148
+
149
+ def test_generic_line(self):
150
+ from gpl.parsers import GENERIC_LINE
151
+ self.assertEqual(generic_line,
152
+ GENERIC_LINE.parseString(generic_line + LF))
153
+
154
+
155
+ class LicenseTest(TestCase):
156
+ def test_license(self):
157
+ from gpl.parsers import LICENSE
158
+
159
+ text = '''#!/usr/bin/python
160
+ # -*- coding: utf-8 -*-
161
+ #
162
+ # ''' + project_line + '''
163
+ # ''' + copyright_line + '''
164
+ #
165
+ # This file is part of pyhwp project.
166
+ #
167
+ # license text.
168
+
169
+ import unittest
170
+ '''
171
+ print LICENSE.parseString(text)
tools/gpl/setup.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup, find_packages
2
+ setup(name='gpl',
3
+ packages=find_packages(),
4
+ install_requires=['docopt'],
5
+ entry_points={
6
+ 'console_scripts': [
7
+ 'gpl = gpl:main'
8
+ ]
9
+ })
tools/jingodf/jingodf/__init__.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #! -*- coding: utf-8 -*-
2
+
3
+ class Jing(object):
4
+
5
+ def __init__(self, executable='jing'):
6
+ ''' External Jing executable
7
+
8
+ :param executable: path to jing executable. Default: jing
9
+ '''
10
+ self.executable = executable
11
+
12
+ def __call__(self, *args, **kwargs):
13
+ args = list(args)
14
+
15
+ import subprocess
16
+ if isinstance(self.executable, basestring):
17
+ args[0:0] = [self.executable]
18
+ else:
19
+ args[0:0] = self.executable
20
+
21
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, **kwargs)
22
+ for line in p.stdout:
23
+ filename, line, column, level, msg = line.split(':', 4)
24
+ level = level.strip()
25
+ msg = msg.strip()
26
+ yield dict(filename=filename,
27
+ line=line, column=column,
28
+ level=level, msg=msg)
29
+ p.wait()
30
+ yield p.returncode
31
+
32
+ def validate(self, rngfile, xmlfile):
33
+ return self('-i', rngfile, xmlfile)
34
+
35
+
36
+ class XmlLint(object):
37
+ def __init__(self, executable='xmllint'):
38
+ ''' External xmllint executable
39
+
40
+ :param executable: path to xmllint executable. Default: xmllint
41
+ '''
42
+ self.executable = executable
43
+
44
+ def __call__(self, *args, **kwargs):
45
+ args = list(args)
46
+
47
+ import subprocess
48
+ if isinstance(self.executable, basestring):
49
+ args[0:0] = [self.executable]
50
+ else:
51
+ args[0:0] = self.executable
52
+
53
+ p = subprocess.Popen(args, stderr=subprocess.PIPE, **kwargs)
54
+ import re
55
+ regex = re.compile('(.*)Relax-NG validity error : (.*)')
56
+ for line in p.stderr:
57
+ if line.endswith(' validates\n'):
58
+ continue
59
+ elif line.endswith(' fails to validate\n'):
60
+ continue
61
+ line = line.strip()
62
+ m = regex.match(line)
63
+ if m:
64
+ location = m.group(1)
65
+ msg = m.group(2)
66
+ if location:
67
+ filename, line_no, element, _ = line.split(':', 3)
68
+ element = element.strip()
69
+ level = 'error'
70
+ msg = msg.strip()
71
+ yield dict(filename=filename,
72
+ line=line_no, column=element,
73
+ level=level, msg=msg)
74
+ else:
75
+ print '*', line
76
+ p.wait()
77
+ yield p.returncode
78
+
79
+ def validate(self, rngfile, xmlfile):
80
+ return self('--noout', '--relaxng', rngfile, xmlfile)
81
+
82
+
83
+ class ODFValidator(object):
84
+ def __init__(self, engine):
85
+ self.engine = engine
86
+
87
+ def validate_manifest_xml(self, version, xmlfile):
88
+ rng_files = {
89
+ '1.0': 'OpenDocument-manifest-schema-v1.0-os.rng',
90
+ '1.1': 'OpenDocument-manifest-schema-v1.1.rng',
91
+ '1.2': 'OpenDocument-v1.2-os-manifest-schema.rng',
92
+ }
93
+
94
+ import pkg_resources
95
+ rng_filename = rng_files[version]
96
+ rng_file = pkg_resources.resource_filename('jingodf',
97
+ 'schema/'+rng_filename)
98
+ return self.engine.validate(rng_file, xmlfile)
99
+
100
+ def validate_opendocument_xml(self, version, xmlfile):
101
+ rng_files = {
102
+ '1.0': 'OpenDocument-schema-v1.0-os.rng',
103
+ '1.1': 'OpenDocument-schema-v1.1.rng',
104
+ '1.2': 'OpenDocument-v1.2-os-schema.rng',
105
+ }
106
+ import pkg_resources
107
+ rng_filename = rng_files[version]
108
+ rng_file = pkg_resources.resource_filename('jingodf',
109
+ 'schema/'+rng_filename)
110
+ return self.engine.validate(rng_file, xmlfile)
111
+
112
+ def validate_odf(self, version, odffile):
113
+
114
+ import os.path
115
+ from zipfile import ZipFile
116
+ zipfile = ZipFile(odffile, 'r')
117
+ try:
118
+ import tempfile
119
+ tmpdir = tempfile.mkdtemp()
120
+ try:
121
+ path = 'META-INF/manifest.xml'
122
+ zipfile.extract(path, tmpdir)
123
+ results = self.validate_manifest_xml(version,
124
+ os.path.join(tmpdir, path))
125
+ for result in results:
126
+ if isinstance(result, dict):
127
+ result['filename'] = path
128
+ yield result
129
+
130
+ path = 'styles.xml'
131
+ zipfile.extract(path, tmpdir)
132
+ results = self.validate_opendocument_xml(version,
133
+ os.path.join(tmpdir, path))
134
+ for result in results:
135
+ if isinstance(result, dict):
136
+ result['filename'] = path
137
+ yield result
138
+
139
+ path = 'content.xml'
140
+ zipfile.extract(path, tmpdir)
141
+ results = self.validate_opendocument_xml(version,
142
+ os.path.join(tmpdir, path))
143
+ for result in results:
144
+ if isinstance(result, dict):
145
+ result['filename'] = path
146
+ yield result
147
+
148
+ finally:
149
+ import shutil
150
+ shutil.rmtree(tmpdir)
151
+ finally:
152
+ zipfile.close()
153
+
154
+ from opster import command
155
+
156
+ @command()
157
+ def validate(odf_file,
158
+ odf_version=('', '1.2', 'OpenDocument specification version'),
159
+ engine=('', 'jing', 'Relax-NG Validator engine')):
160
+ ''' Validate an ODF file against the OpenDocument Relax-NG Schema. '''
161
+
162
+ engines = dict(jing=Jing, xmllint=XmlLint)
163
+ engine_class = engines.get(engine, Jing)
164
+ engine = engine_class()
165
+ odf_validator = ODFValidator(engine)
166
+ results = odf_validator.validate_odf(odf_version, odf_file)
167
+
168
+ def print_result(result):
169
+ if isinstance(result, dict):
170
+ print '%(filename)s:%(line)s:%(column)s: %(level)s: %(msg)s'%result
171
+
172
+ for result in results:
173
+ print_result(result)
174
+
175
+ def main():
176
+ validate.command()
tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.0-os.rng ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ OASIS OpenDocument v1.0
4
+ OASIS standard, 1 May 2005
5
+ Relax-NG Manifest Schema
6
+
7
+ $Id$
8
+
9
+ © 2002-2005 OASIS Open
10
+ © 1999-2005 Sun Microsystems, Inc.
11
+ -->
12
+
13
+ <grammar
14
+ xmlns="http://relaxng.org/ns/structure/1.0"
15
+
16
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
17
+
18
+ xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
19
+ <define name="manifest">
20
+ <element name="manifest:manifest">
21
+ <oneOrMore>
22
+ <ref name="file-entry"/>
23
+ </oneOrMore>
24
+ </element>
25
+ </define>
26
+
27
+ <start>
28
+ <choice>
29
+ <ref name="manifest"/>
30
+ </choice>
31
+ </start>
32
+ <define name="file-entry">
33
+ <element name="manifest:file-entry">
34
+ <ref name="file-entry-attlist"/>
35
+ <optional>
36
+ <ref name="encryption-data"/>
37
+ </optional>
38
+ </element>
39
+ </define>
40
+ <define name="file-entry-attlist" combine="interleave">
41
+ <attribute name="manifest:full-path">
42
+ <data type="string"/>
43
+ </attribute>
44
+ </define>
45
+ <define name="file-entry-attlist" combine="interleave">
46
+ <optional>
47
+ <attribute name="manifest:size">
48
+ <data type="nonNegativeInteger"/>
49
+ </attribute>
50
+ </optional>
51
+ </define>
52
+ <define name="file-entry-attlist" combine="interleave">
53
+ <attribute name="manifest:media-type">
54
+ <data type="string"/>
55
+ </attribute>
56
+ </define>
57
+ <define name="encryption-data">
58
+ <element name="manifest:encryption-data">
59
+ <ref name="encryption-data-attlist"/>
60
+ <ref name="algorithm"/>
61
+ <ref name="key-derivation"/>
62
+ </element>
63
+ </define>
64
+ <define name="encryption-data-attlist" combine="interleave">
65
+ <attribute name="manifest:checksum-type">
66
+ <data type="string"/>
67
+ </attribute>
68
+ </define>
69
+ <define name="encryption-data-attlist" combine="interleave">
70
+ <attribute name="manifest:checksum">
71
+ <data type="base64Binary"/>
72
+ </attribute>
73
+ </define>
74
+ <define name="algorithm">
75
+ <element name="manifest:algorithm">
76
+ <ref name="algorithm-attlist"/>
77
+ <empty/>
78
+ </element>
79
+ </define>
80
+ <define name="algorithm-attlist" combine="interleave">
81
+ <attribute name="manifest:algorithm-name">
82
+ <data type="string"/>
83
+ </attribute>
84
+ </define>
85
+ <define name="algorithm-attlist" combine="interleave">
86
+ <attribute name="manifest:initialisation-vector">
87
+ <data type="base64Binary"/>
88
+ </attribute>
89
+ </define>
90
+ <define name="key-derivation">
91
+ <element name="manifest:key-derivation">
92
+ <ref name="key-derivation-attlist"/>
93
+ <empty/>
94
+ </element>
95
+ </define>
96
+ <define name="key-derivation-attlist" combine="interleave">
97
+ <attribute name="manifest:key-derivation-name">
98
+ <data type="string"/>
99
+ </attribute>
100
+ </define>
101
+ <define name="key-derivation-attlist" combine="interleave">
102
+ <attribute name="manifest:salt">
103
+ <data type="base64Binary"/>
104
+ </attribute>
105
+ </define>
106
+ <define name="key-derivation-attlist" combine="interleave">
107
+ <attribute name="manifest:iteration-count">
108
+ <data type="nonNegativeInteger"/>
109
+ </attribute>
110
+ </define>
111
+ </grammar>
tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.1.rng ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ OASIS OpenDocument v1.1
4
+ OASIS Standard, 1 Feb 2007
5
+ Relax-NG Manifest Schema
6
+
7
+ $Id$
8
+
9
+ © 2002-2007 OASIS Open
10
+ © 1999-2007 Sun Microsystems, Inc.
11
+ -->
12
+
13
+ <grammar
14
+ xmlns="http://relaxng.org/ns/structure/1.0"
15
+
16
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
17
+
18
+ xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
19
+ <define name="manifest">
20
+ <element name="manifest:manifest">
21
+ <oneOrMore>
22
+ <ref name="file-entry"/>
23
+ </oneOrMore>
24
+ </element>
25
+ </define>
26
+
27
+ <start>
28
+ <choice>
29
+ <ref name="manifest"/>
30
+ </choice>
31
+ </start>
32
+ <define name="file-entry">
33
+ <element name="manifest:file-entry">
34
+ <ref name="file-entry-attlist"/>
35
+ <optional>
36
+ <ref name="encryption-data"/>
37
+ </optional>
38
+ </element>
39
+ </define>
40
+ <define name="file-entry-attlist" combine="interleave">
41
+ <attribute name="manifest:full-path">
42
+ <data type="string"/>
43
+ </attribute>
44
+ </define>
45
+ <define name="file-entry-attlist" combine="interleave">
46
+ <optional>
47
+ <attribute name="manifest:size">
48
+ <data type="nonNegativeInteger"/>
49
+ </attribute>
50
+ </optional>
51
+ </define>
52
+ <define name="file-entry-attlist" combine="interleave">
53
+ <attribute name="manifest:media-type">
54
+ <data type="string"/>
55
+ </attribute>
56
+ </define>
57
+ <define name="encryption-data">
58
+ <element name="manifest:encryption-data">
59
+ <ref name="encryption-data-attlist"/>
60
+ <ref name="algorithm"/>
61
+ <ref name="key-derivation"/>
62
+ </element>
63
+ </define>
64
+ <define name="encryption-data-attlist" combine="interleave">
65
+ <attribute name="manifest:checksum-type">
66
+ <data type="string"/>
67
+ </attribute>
68
+ </define>
69
+ <define name="encryption-data-attlist" combine="interleave">
70
+ <attribute name="manifest:checksum">
71
+ <data type="base64Binary"/>
72
+ </attribute>
73
+ </define>
74
+ <define name="algorithm">
75
+ <element name="manifest:algorithm">
76
+ <ref name="algorithm-attlist"/>
77
+ <empty/>
78
+ </element>
79
+ </define>
80
+ <define name="algorithm-attlist" combine="interleave">
81
+ <attribute name="manifest:algorithm-name">
82
+ <data type="string"/>
83
+ </attribute>
84
+ </define>
85
+ <define name="algorithm-attlist" combine="interleave">
86
+ <attribute name="manifest:initialisation-vector">
87
+ <data type="base64Binary"/>
88
+ </attribute>
89
+ </define>
90
+ <define name="key-derivation">
91
+ <element name="manifest:key-derivation">
92
+ <ref name="key-derivation-attlist"/>
93
+ <empty/>
94
+ </element>
95
+ </define>
96
+ <define name="key-derivation-attlist" combine="interleave">
97
+ <attribute name="manifest:key-derivation-name">
98
+ <data type="string"/>
99
+ </attribute>
100
+ </define>
101
+ <define name="key-derivation-attlist" combine="interleave">
102
+ <attribute name="manifest:salt">
103
+ <data type="base64Binary"/>
104
+ </attribute>
105
+ </define>
106
+ <define name="key-derivation-attlist" combine="interleave">
107
+ <attribute name="manifest:iteration-count">
108
+ <data type="nonNegativeInteger"/>
109
+ </attribute>
110
+ </define>
111
+ </grammar>
tools/jingodf/jingodf/schema/OpenDocument-schema-v1.0-os.rng ADDED
The diff for this file is too large to render. See raw diff
 
tools/jingodf/jingodf/schema/OpenDocument-schema-v1.1.rng ADDED
The diff for this file is too large to render. See raw diff
 
tools/jingodf/jingodf/schema/OpenDocument-strict-schema-v1.1.rng ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ OASIS OpenDocument v1.1
4
+ OASIS Standard, 1 Feb 2007
5
+ Strict Relax-NG Schema
6
+
7
+ $Id$
8
+
9
+ © 2002-2007 OASIS Open
10
+ © 1999-2007 Sun Microsystems, Inc.
11
+ -->
12
+
13
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
14
+ <include href="OpenDocument-schema-v1.1.rng">
15
+ <define name="office-meta-content">
16
+ <ref name="office-meta-content-strict"/>
17
+ </define>
18
+ <define name="style-page-layout-properties-content">
19
+ <ref name="style-page-layout-properties-content-strict"/>
20
+ </define>
21
+ <define name="style-header-footer-properties-content">
22
+ <ref name="style-header-footer-properties-content-strict"/>
23
+ </define>
24
+ <define name="style-drawing-page-properties-content">
25
+ <ref name="style-drawing-page-properties-content-strict"/>
26
+ </define>
27
+ <define name="style-text-properties-content">
28
+ <ref name="style-text-properties-content-strict"/>
29
+ </define>
30
+ <define name="style-paragraph-properties-content">
31
+ <ref name="style-paragraph-properties-content-strict"/>
32
+ </define>
33
+ <define name="style-ruby-properties-content">
34
+ <ref name="style-ruby-properties-content-strict"/>
35
+ </define>
36
+ <define name="style-section-properties-content">
37
+ <ref name="style-section-properties-content-strict"/>
38
+ </define>
39
+ <define name="style-list-level-properties-content">
40
+ <ref name="style-list-level-properties-content-strict"/>
41
+ </define>
42
+ <define name="style-table-properties-content">
43
+ <ref name="style-table-properties-content-strict"/>
44
+ </define>
45
+ <define name="style-table-column-properties-content">
46
+ <ref name="style-table-column-properties-content-strict"/>
47
+ </define>
48
+ <define name="style-table-row-properties-content">
49
+ <ref name="style-table-row-properties-content-strict"/>
50
+ </define>
51
+ <define name="style-table-cell-properties-content">
52
+ <ref name="style-table-cell-properties-content-strict"/>
53
+ </define>
54
+ <define name="style-graphic-properties-content">
55
+ <ref name="style-graphic-properties-content-strict"/>
56
+ </define>
57
+ <define name="style-chart-properties-content">
58
+ <ref name="style-chart-properties-content-strict"/>
59
+ </define>
60
+ </include>
61
+ </grammar>
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-dsig-schema.rng ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Open Document Format for Office Applications (OpenDocument) Version 1.2
4
+ OASIS Standard, 29 September 2011
5
+ Digital Signatures Relax-NG Schema
6
+ Source: http://docs.oasis-open.org/office/v1.2/os/
7
+ Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
8
+
9
+ All capitalized terms in the following text have the meanings assigned to them
10
+ in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
11
+ full Policy may be found at the OASIS website.
12
+
13
+ This document and translations of it may be copied and furnished to others, and
14
+ derivative works that comment on or otherwise explain it or assist in its
15
+ implementation may be prepared, copied, published, and distributed, in whole or
16
+ in part, without restriction of any kind, provided that the above copyright
17
+ notice and this section are included on all such copies and derivative works.
18
+ However, this document itself may not be modified in any way, including by
19
+ removing the copyright notice or references to OASIS, except as needed for the
20
+ purpose of developing any document or deliverable produced by an OASIS
21
+ Technical Committee (in which case the rules applicable to copyrights, as set
22
+ forth in the OASIS IPR Policy, must be followed) or as required to translate it
23
+ into languages other than English.
24
+
25
+ The limited permissions granted above are perpetual and will not be revoked by
26
+ OASIS or its successors or assigns.
27
+
28
+ This document and the information contained herein is provided on an "AS IS"
29
+ basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
30
+ LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
31
+ INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
32
+ FITNESS FOR A PARTICULAR PURPOSE.
33
+ -->
34
+
35
+ <grammar
36
+ xmlns="http://relaxng.org/ns/structure/1.0"
37
+
38
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
39
+
40
+ xmlns:dsig="urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"
41
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
42
+ >
43
+ <start>
44
+ <choice>
45
+ <ref name="dsig-document-signatures"/>
46
+ </choice>
47
+ </start>
48
+ <define name="dsig-document-signatures">
49
+ <element name="dsig:document-signatures">
50
+ <ref name="dsig-document-signatures-attlist"/>
51
+ <oneOrMore>
52
+ <ref name="ds-signature"/>
53
+ </oneOrMore>
54
+ </element>
55
+ </define>
56
+ <define name="dsig-document-signatures-attlist">
57
+ <attribute name="dsig:version">
58
+ <value>1.2</value>
59
+ </attribute>
60
+ </define>
61
+ <define name="ds-signature">
62
+ <element name="ds:Signature">
63
+ <!-- The permitted content of this element is the permitted -->
64
+ <!-- content of the Signature element defined by W3C XML -->
65
+ <!-- Signature Syntax and Processing (Second Edition). -->
66
+ <!-- See OpenDocument v1.2 part 3, section 4.3. -->
67
+ <ref name="dsMarkup"/>
68
+ </element>
69
+ </define>
70
+ <define name="dsMarkup">
71
+ <zeroOrMore>
72
+ <choice>
73
+ <attribute>
74
+ <anyName/>
75
+ </attribute>
76
+ <text/>
77
+ <element>
78
+ <anyName/>
79
+ <ref name="dsMarkup"/>
80
+ </element>
81
+ </choice>
82
+ </zeroOrMore>
83
+ </define>
84
+ </grammar>
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-manifest-schema.rng ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Open Document Format for Office Applications (OpenDocument) Version 1.2
4
+ OASIS Standard, 29 September 2011
5
+ Manifest Relax-NG Schema
6
+ Source: http://docs.oasis-open.org/office/v1.2/os/
7
+ Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
8
+
9
+ All capitalized terms in the following text have the meanings assigned to them
10
+ in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
11
+ full Policy may be found at the OASIS website.
12
+
13
+ This document and translations of it may be copied and furnished to others, and
14
+ derivative works that comment on or otherwise explain it or assist in its
15
+ implementation may be prepared, copied, published, and distributed, in whole or
16
+ in part, without restriction of any kind, provided that the above copyright
17
+ notice and this section are included on all such copies and derivative works.
18
+ However, this document itself may not be modified in any way, including by
19
+ removing the copyright notice or references to OASIS, except as needed for the
20
+ purpose of developing any document or deliverable produced by an OASIS
21
+ Technical Committee (in which case the rules applicable to copyrights, as set
22
+ forth in the OASIS IPR Policy, must be followed) or as required to translate it
23
+ into languages other than English.
24
+
25
+ The limited permissions granted above are perpetual and will not be revoked by
26
+ OASIS or its successors or assigns.
27
+
28
+ This document and the information contained herein is provided on an "AS IS"
29
+ basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
30
+ LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
31
+ INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
32
+ FITNESS FOR A PARTICULAR PURPOSE.
33
+ -->
34
+ <grammar
35
+ xmlns="http://relaxng.org/ns/structure/1.0"
36
+
37
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
38
+
39
+ xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
40
+ >
41
+ <start>
42
+ <choice>
43
+ <ref name="manifest"/>
44
+ </choice>
45
+ </start>
46
+ <define name="manifest">
47
+ <element name="manifest:manifest">
48
+ <ref name="manifest-attlist"/>
49
+ <oneOrMore>
50
+ <ref name="file-entry"/>
51
+ </oneOrMore>
52
+ </element>
53
+ </define>
54
+ <define name="manifest-attlist">
55
+ <attribute name="manifest:version">
56
+ <value>1.2</value>
57
+ </attribute>
58
+ </define>
59
+ <define name="file-entry">
60
+ <element name="manifest:file-entry">
61
+ <ref name="file-entry-attlist"/>
62
+ <optional>
63
+ <ref name="encryption-data"/>
64
+ </optional>
65
+ </element>
66
+ </define>
67
+ <define name="file-entry-attlist">
68
+ <interleave>
69
+ <attribute name="manifest:full-path">
70
+ <ref name="string"/>
71
+ </attribute>
72
+ <optional>
73
+ <attribute name="manifest:size">
74
+ <ref name="nonNegativeInteger"/>
75
+ </attribute>
76
+ </optional>
77
+ <attribute name="manifest:media-type">
78
+ <ref name="string"/>
79
+ </attribute>
80
+ <optional>
81
+ <attribute name="manifest:preferred-view-mode">
82
+ <choice>
83
+ <value>edit</value>
84
+ <value>presentation-slide-show</value>
85
+ <value>read-only</value>
86
+ <ref name="namespacedToken"/>
87
+ </choice>
88
+ </attribute>
89
+ </optional>
90
+ <optional>
91
+ <attribute name="manifest:version">
92
+ <ref name="string"/>
93
+ </attribute>
94
+ </optional>
95
+ </interleave>
96
+ </define>
97
+
98
+ <define name="encryption-data">
99
+ <element name="manifest:encryption-data">
100
+ <ref name="encryption-data-attlist"/>
101
+ <ref name="algorithm"/>
102
+ <optional>
103
+ <ref name="start-key-generation"/>
104
+ </optional>
105
+ <ref name="key-derivation"/>
106
+ </element>
107
+ </define>
108
+ <define name="encryption-data-attlist">
109
+ <interleave>
110
+ <attribute name="manifest:checksum-type">
111
+ <choice>
112
+ <value>SHA1/1K</value>
113
+ <ref name="anyURI"/>
114
+ </choice>
115
+ </attribute>
116
+ <attribute name="manifest:checksum">
117
+ <ref name="base64Binary"/>
118
+ </attribute>
119
+ </interleave>
120
+ </define>
121
+ <define name="algorithm">
122
+ <element name="manifest:algorithm">
123
+ <ref name="algorithm-attlist"/>
124
+ <ref name="anyElements"/>
125
+ </element>
126
+ </define>
127
+ <define name="algorithm-attlist">
128
+ <interleave>
129
+ <attribute name="manifest:algorithm-name">
130
+ <choice>
131
+ <value>Blowfish CFB</value>
132
+ <ref name="anyURI"/>
133
+ </choice>
134
+ </attribute>
135
+ <attribute name="manifest:initialisation-vector">
136
+ <ref name="base64Binary"/>
137
+ </attribute>
138
+ </interleave>
139
+ </define>
140
+ <define name="anyAttListOrElements">
141
+ <zeroOrMore>
142
+ <attribute>
143
+ <anyName/>
144
+ <text/>
145
+ </attribute>
146
+ </zeroOrMore>
147
+ <ref name="anyElements"/>
148
+ </define>
149
+ <define name="anyElements">
150
+ <zeroOrMore>
151
+ <element>
152
+ <anyName/>
153
+ <mixed>
154
+ <ref name="anyAttListOrElements"/>
155
+ </mixed>
156
+ </element>
157
+ </zeroOrMore>
158
+ </define>
159
+ <define name="key-derivation">
160
+ <element name="manifest:key-derivation">
161
+ <ref name="key-derivation-attlist"/>
162
+ <empty/>
163
+ </element>
164
+ </define>
165
+ <define name="key-derivation-attlist">
166
+ <interleave>
167
+ <attribute name="manifest:key-derivation-name">
168
+ <choice>
169
+ <value>PBKDF2</value>
170
+ <ref name="anyURI"/>
171
+ </choice>
172
+ </attribute>
173
+ <attribute name="manifest:salt">
174
+ <ref name="base64Binary"/>
175
+ </attribute>
176
+ <attribute name="manifest:iteration-count">
177
+ <ref name="nonNegativeInteger"/>
178
+ </attribute>
179
+ <optional>
180
+ <attribute name="manifest:key-size">
181
+ <ref name="nonNegativeInteger"/>
182
+ </attribute>
183
+ </optional>
184
+ </interleave>
185
+ </define>
186
+ <define name="start-key-generation">
187
+ <element name="manifest:start-key-generation">
188
+ <ref name="start-key-generation-attlist"/>
189
+ <empty/>
190
+ </element>
191
+ </define>
192
+ <define name="start-key-generation-attlist">
193
+ <interleave>
194
+ <attribute name="manifest:start-key-generation-name">
195
+ <choice>
196
+ <value>SHA1</value>
197
+ <ref name="anyURI"/>
198
+ </choice>
199
+ </attribute>
200
+ <optional>
201
+ <attribute name="manifest:key-size">
202
+ <ref name="nonNegativeInteger"/>
203
+ </attribute>
204
+ </optional>
205
+ </interleave>
206
+ </define>
207
+ <define name="base64Binary">
208
+ <data type="base64Binary"/>
209
+ </define>
210
+ <define name="namespacedToken">
211
+ <data type="QName">
212
+ <param name="pattern">[^:]+:[^:]+</param>
213
+ </data>
214
+ </define>
215
+ <define name="nonNegativeInteger">
216
+ <data type="nonNegativeInteger"/>
217
+ </define>
218
+ <define name="string">
219
+ <data type="string"/>
220
+ </define>
221
+ <define name="anyURI">
222
+ <data type="anyURI"/>
223
+ </define>
224
+ </grammar>
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-metadata.owl ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Open Document Format for Office Applications (OpenDocument) Version 1.2
4
+ OASIS Standard, 29 September 2011
5
+ OWL Metadata Manifest Description
6
+ Source: http://docs.oasis-open.org/office/v1.2/os/
7
+ Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
8
+
9
+ All capitalized terms in the following text have the meanings assigned to them
10
+ in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
11
+ full Policy may be found at the OASIS website.
12
+
13
+ This document and translations of it may be copied and furnished to others, and
14
+ derivative works that comment on or otherwise explain it or assist in its
15
+ implementation may be prepared, copied, published, and distributed, in whole or
16
+ in part, without restriction of any kind, provided that the above copyright
17
+ notice and this section are included on all such copies and derivative works.
18
+ However, this document itself may not be modified in any way, including by
19
+ removing the copyright notice or references to OASIS, except as needed for the
20
+ purpose of developing any document or deliverable produced by an OASIS
21
+ Technical Committee (in which case the rules applicable to copyrights, as set
22
+ forth in the OASIS IPR Policy, must be followed) or as required to translate it
23
+ into languages other than English.
24
+
25
+ The limited permissions granted above are perpetual and will not be revoked by
26
+ OASIS or its successors or assigns.
27
+
28
+ This document and the information contained herein is provided on an "AS IS"
29
+ basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
30
+ LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
31
+ INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
32
+ FITNESS FOR A PARTICULAR PURPOSE.
33
+ -->
34
+
35
+ <rdf:RDF
36
+ xmlns="http://www.w3.org/2000/01/rdf-schema#"
37
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
38
+ xmlns:owl="http://www.w3.org/2002/07/owl#"
39
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
40
+ xmlns:pkg="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#"
41
+ xmlns:odf="http://docs.oasis-open.org/ns/office/1.2/meta/odf#">
42
+
43
+ <owl:Ontology rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#">
44
+ <owl:imports rdf:resource="http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-package-metadata.owl" />
45
+ <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
46
+ <dc:title xml:lang="en">Open Document Schema Metadata Manifest Ontology</dc:title>
47
+ <label xml:lang="en">ODF Schema Metadata Manifest</label>
48
+ </owl:Ontology>
49
+
50
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#ContentFile">
51
+ <label xml:lang="en">The OpenDocument Content File</label>
52
+ <comment xml:lang="en">The unique content.xml from the root path of the document</comment>
53
+ <subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
54
+ </owl:Class>
55
+
56
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#StylesFile">
57
+ <label xml:lang="en">The OpenDocument Styles File</label>
58
+ <comment xml:lang="en">The unique styles.xml from the root path of the document</comment>
59
+ <subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
60
+ </owl:Class>
61
+
62
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#Element">
63
+ <label xml:lang="en">OpenDocument Package ODF XML Element</label>
64
+ <subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Element"/>
65
+ </owl:Class>
66
+
67
+ <owl:Class rdf:about="urn:oasis:names:tc:opendocument:xmlns:text:1.0meta-field">
68
+ <label xml:lang="en">OpenDocument Meta field Element</label>
69
+ <subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/odf#Element"/>
70
+ </owl:Class>
71
+
72
+ <owl:DatatypeProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#prefix">
73
+ <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
74
+ <domain rdf:resource="urn:oasis:names:tc:opendocument:xmlns:text:1.0meta-field"/>
75
+ <range rdf:resource="http://www.w3.org/2001/XMLSchema-datatypes#string"/>
76
+ <label xml:lang="en">has prefix</label>
77
+ </owl:DatatypeProperty>
78
+
79
+ <owl:DatatypeProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#suffix">
80
+ <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
81
+ <domain rdf:resource="urn:oasis:names:tc:opendocument:xmlns:text:1.0meta-field"/>
82
+ <range rdf:resource="http://www.w3.org/2001/XMLSchema-datatypes#string"/>
83
+ <label xml:lang="en">has suffix</label>
84
+ </owl:DatatypeProperty>
85
+ </rdf:RDF>
86
+
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-package-metadata.owl ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Open Document Format for Office Applications (OpenDocument) Version 1.2
4
+ OASIS Standard, 29 September 2011
5
+ OWL Metadata Manifest Description
6
+ Source: http://docs.oasis-open.org/office/v1.2/os/
7
+ Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
8
+
9
+ All capitalized terms in the following text have the meanings assigned to them
10
+ in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
11
+ full Policy may be found at the OASIS website.
12
+
13
+ This document and translations of it may be copied and furnished to others, and
14
+ derivative works that comment on or otherwise explain it or assist in its
15
+ implementation may be prepared, copied, published, and distributed, in whole or
16
+ in part, without restriction of any kind, provided that the above copyright
17
+ notice and this section are included on all such copies and derivative works.
18
+ However, this document itself may not be modified in any way, including by
19
+ removing the copyright notice or references to OASIS, except as needed for the
20
+ purpose of developing any document or deliverable produced by an OASIS
21
+ Technical Committee (in which case the rules applicable to copyrights, as set
22
+ forth in the OASIS IPR Policy, must be followed) or as required to translate it
23
+ into languages other than English.
24
+
25
+ The limited permissions granted above are perpetual and will not be revoked by
26
+ OASIS or its successors or assigns.
27
+
28
+ This document and the information contained herein is provided on an "AS IS"
29
+ basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
30
+ LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
31
+ INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
32
+ FITNESS FOR A PARTICULAR PURPOSE.
33
+ -->
34
+
35
+ <rdf:RDF
36
+ xmlns="http://www.w3.org/2000/01/rdf-schema#"
37
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
38
+ xmlns:owl="http://www.w3.org/2002/07/owl#"
39
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
40
+ xmlns:pkg="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#">
41
+
42
+ <owl:Ontology rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#">
43
+ <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
44
+ <dc:title xml:lang="en">Open Document Package Metadata Manifest Ontology</dc:title>
45
+ <label xml:lang="en">ODF Package Metadata Manifest</label>
46
+ </owl:Ontology>
47
+
48
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Document">
49
+ <label xml:lang="en">OpenDocument Document</label>
50
+ </owl:Class>
51
+
52
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File">
53
+ <label xml:lang="en"> OpenDocument Document Package File</label>
54
+ </owl:Class>
55
+
56
+ <owl:ObjectProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#hasPart">
57
+ <domain rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Document"/>
58
+ <range rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
59
+ <label xml:lang="en">contains</label>
60
+ <comment xml:lang="en">Related to dcterms:hasPart of the Dublin Core Metadata Initiative</comment>
61
+ </owl:ObjectProperty>
62
+
63
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#MetadataFile">
64
+ <label xml:lang="en">An OpenDocument Metadata File</label>
65
+ <comment xml:lang="en">Used for any metadata file in the document</comment>
66
+ <subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
67
+ </owl:Class>
68
+
69
+ <owl:DatatypeProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#mimeType">
70
+ <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
71
+ <domain rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
72
+ <range rdf:resource="http://www.w3.org/2001/XMLSchema-datatypes#string"/>
73
+ <label xml:lang="en">the MIME type</label>
74
+ <comment xml:lang="en">A string representing the MIME media type of a file (see RFC4288).</comment>
75
+ </owl:DatatypeProperty>
76
+
77
+ <owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Element">
78
+ <label xml:lang="en">OpenDocument Package XML Element</label>
79
+ </owl:Class>
80
+
81
+ </rdf:RDF>
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-schema.rng ADDED
The diff for this file is too large to render. See raw diff
 
tools/jingodf/setup.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup
2
+
3
+ setup(name='jingodf',
4
+ version='0.0',
5
+ license='GNU Affero GPL v3',
6
+ author = 'mete0r',
7
+ author_email = 'https://github.com/mete0r',
8
+ packages = ['jingodf'],
9
+ package_data = dict(jingodf=['schema/*']),
10
+ install_requires=['opster == 3.7'],
11
+ entry_points = {
12
+ 'console_scripts': ['jingodf = jingodf:main']
13
+ })
tools/jxml/jxml.coverage/jxml_coverage.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import with_statement
3
+
4
+ import logging
5
+ import sys
6
+ import os.path
7
+
8
+ import colorama
9
+ from colorama import Fore, Back, Style
10
+ from docopt import docopt
11
+
12
+ from jxml.etree import XSLTCoverage
13
+ from jxml.etree import xsltcoverage
14
+
15
+
16
+ def colorama_init(f):
17
+ def wrapper(*args, **kwargs):
18
+ colorama.init()
19
+ try:
20
+ return f(*args, **kwargs)
21
+ finally:
22
+ colorama.deinit()
23
+ return wrapper
24
+
25
+
26
+ @colorama_init
27
+ def annotate_main():
28
+ __doc__ = '''
29
+ Usage: jxml-annotate [options] <xml-file>...
30
+
31
+ <xml-file> Cobertura-compatible coverage data file
32
+ --color=[auto|yes|no] Output with colors
33
+
34
+ Example:
35
+ jxml-annotate --color=yes coverage.xml | less -R
36
+ '''
37
+ args = docopt(__doc__)
38
+
39
+ logging.basicConfig()
40
+ logger = logging.getLogger('jxml.annotate')
41
+
42
+ use_color = args['--color'] == 'yes'
43
+ if args['--color'] in ('auto', None):
44
+ use_color = sys.stdout.isatty()
45
+
46
+ coverage = XSLTCoverage()
47
+ for arg in args['<xml-file>']:
48
+ coverage.read_from(arg)
49
+
50
+ traces = coverage.traces
51
+ for filename in sorted(traces):
52
+ covered_lines = traces[filename]
53
+ if not os.path.exists(filename):
54
+ logger.info('skipping %s: not exists', filename)
55
+ continue
56
+ print filename
57
+
58
+ with open(filename) as f:
59
+ for line_no, line in enumerate(f):
60
+ line_no += 1
61
+ count = covered_lines.get(line_no, 0)
62
+ annotated = '%8d: %s' % (count, line)
63
+
64
+ if use_color:
65
+ if count == 0:
66
+ color = Fore.RED
67
+ else:
68
+ color = Fore.RESET
69
+ annotated = color + annotated + Fore.RESET
70
+ sys.stdout.write(annotated)
71
+
72
+ print ''
73
+
74
+
75
+ def load_tests(filenames):
76
+ import unittest
77
+ ts = unittest.TestSuite()
78
+ testloader = unittest.defaultTestLoader
79
+ for filename in filenames:
80
+ d = dict()
81
+ execfile(filename, d)
82
+ for name in d:
83
+ x = d[name]
84
+ if isinstance(x, type) and issubclass(x, unittest.TestCase):
85
+ ts.addTests(testloader.loadTestsFromTestCase(x))
86
+ return ts
87
+
88
+
89
+ def cov_test_main():
90
+ __doc__ = '''
91
+ Usage: jxml-cov-test [options] <output-file> <unittest-file>...
92
+
93
+ <output-file> Cobertura-compatible coverage data file.
94
+ <unittest-file> unittest files.
95
+
96
+ Example:
97
+ jxml-cov-test coverage.xml test1.py test2.py
98
+ '''
99
+ args = docopt(__doc__)
100
+
101
+ logging.basicConfig()
102
+ logger = logging.getLogger('jxml.cov-test')
103
+
104
+ from java.lang import System
105
+ import unittest
106
+
107
+ props = System.getProperties()
108
+ props['javax.xml.transform.TransformerFactory'] = 'org.apache.xalan.processor.TransformerFactoryImpl'
109
+ props['javax.xml.parsers.DocumentBuilderFactory'] = 'org.apache.xerces.jaxp.DocumentBuilderFactoryImpl'
110
+ props['javax.xml.parsers.SAXParserFactory'] = 'org.apache.xerces.jaxp.SAXParserFactoryImpl'
111
+
112
+ output_name = args['<output-file>']
113
+ test_filenames = args['<unittest-file>']
114
+ ts = load_tests(test_filenames)
115
+ runner = unittest.TextTestRunner()
116
+ with xsltcoverage(output_name) as coverage:
117
+ runner.run(ts)
tools/jxml/jxml.coverage/setup.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from setuptools import setup, find_packages
3
+ import os.path
4
+ import sys
5
+ if not sys.platform.startswith('java'):
6
+ raise SystemExit(1)
7
+
8
+ os.chdir(os.path.dirname(__file__))
9
+
10
+ console_scripts = ['jxml-annotate = jxml_coverage:annotate_main',
11
+ 'jxml-cov-test = jxml_coverage:cov_test_main']
12
+
13
+ setup(name='jxml.coverage',
14
+ py_modules=['jxml_coverage'],
15
+ install_requires=['jxml', 'docopt', 'colorama'],
16
+ entry_points=dict(console_scripts=console_scripts))
tools/jxml/jxml/__init__.py ADDED
File without changes
tools/jxml/jxml/etree/__init__.py ADDED
@@ -0,0 +1,1072 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8
2
+ from __future__ import with_statement
3
+
4
+ from contextlib import contextmanager
5
+ import logging
6
+ from itertools import imap
7
+ from itertools import ifilter
8
+ from itertools import takewhile
9
+ import os.path
10
+ from urlparse import urlparse
11
+ from urlparse import urljoin
12
+ from pkgutil import simplegeneric
13
+
14
+ from javax.xml import XMLConstants
15
+ from javax.xml.parsers import DocumentBuilderFactory
16
+ from javax.xml.xpath import XPathFactory
17
+ from javax.xml.xpath import XPathConstants
18
+ from javax.xml.xpath import XPathExpressionException
19
+ from javax.xml.namespace import NamespaceContext
20
+ from javax.xml.transform import OutputKeys
21
+ from javax.xml.transform import TransformerFactory
22
+ from javax.xml.transform import URIResolver
23
+ from javax.xml.transform.dom import DOMSource
24
+ from javax.xml.transform.dom import DOMResult
25
+ from javax.xml.transform.stream import StreamSource
26
+ from javax.xml.transform.stream import StreamResult
27
+ from org.w3c import dom
28
+ from java.io import File
29
+ from java.io import InputStream
30
+ from java.io import IOException
31
+ from java.io import FileInputStream
32
+ from java.io import FileOutputStream
33
+ from java.io import ByteArrayInputStream
34
+ from java.io import ByteArrayOutputStream
35
+ from java.io import ByteArrayInputStream
36
+ from java.util import Iterator
37
+ from java.util import NoSuchElementException
38
+
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+ fac = DocumentBuilderFactory.newInstance()
43
+ fac.setNamespaceAware(True)
44
+ builder = fac.newDocumentBuilder()
45
+ transformfac = TransformerFactory.newInstance()
46
+ xpathfac = XPathFactory.newInstance()
47
+
48
+
49
+ class XSLTCoverage(object):
50
+
51
+ def __init__(self):
52
+ self.traces = dict()
53
+
54
+ def trace(self, systemId, startline, endline):
55
+ files = self.traces
56
+ lines = files.setdefault(systemId, dict())
57
+ lines[startline] = lines.get(startline, 0) + 1
58
+ lines[endline] = lines.get(endline, 0) + 1
59
+
60
+ def writeto(self, f):
61
+ trace_root = Element('coverage')
62
+ packages = SubElement(trace_root, 'packages')
63
+ package_attrib = {'name': '', 'branch-rate': '0', 'complexity': '0',
64
+ 'line-rate': '0'}
65
+ package = SubElement(packages, 'package', package_attrib)
66
+ classes = SubElement(package, 'classes')
67
+
68
+ for filename in sorted(self.traces):
69
+ if not filename:
70
+ continue
71
+ if filename.startswith('file://'):
72
+ fn = filename[len('file://'):]
73
+ else:
74
+ fn = filename
75
+ class_attrib = dict()
76
+ class_attrib['filename'] = fn
77
+ class_attrib['branch-rate'] = '0'
78
+ class_attrib['complexity'] = '0'
79
+ class_attrib['line-rate'] = '0'
80
+ class_attrib['name'] = os.path.basename(fn)
81
+ clas = SubElement(classes, 'class', class_attrib)
82
+ methods = SubElement(clas, 'methods')
83
+ linesElem = SubElement(clas, 'lines')
84
+ lines = self.traces[filename]
85
+ for lineno in sorted(lines):
86
+ count = lines[lineno]
87
+ lineElem = SubElement(linesElem, 'line',
88
+ dict(number=str(lineno),
89
+ hits=str(count)))
90
+ s = tostring(trace_root, encoding='utf-8', xml_declaration=True)
91
+ f.write(s)
92
+ f.write('\n')
93
+
94
+ def read_from(self, filename):
95
+ tree = parse(filename)
96
+ coverage = tree.getroot()
97
+ assert coverage.tag == 'coverage'
98
+ packages = coverage[0]
99
+ assert packages.tag == 'packages'
100
+ for package in packages:
101
+ if package.tag != 'package':
102
+ continue
103
+ classes = package[0]
104
+ assert classes.tag == 'classes'
105
+ for clas in classes:
106
+ if clas.tag != 'class':
107
+ continue
108
+ filename = clas.get('filename')
109
+ trace = self.traces.setdefault(filename, dict())
110
+ for lines in clas:
111
+ if lines.tag != 'lines':
112
+ continue
113
+ for line in lines:
114
+ if line.tag != 'line':
115
+ continue
116
+ lineno = int(line.get('number'))
117
+ hits = int(line.get('hits'))
118
+ trace[lineno] = trace.get(lineno, 0) + hits
119
+
120
+
121
+ xsltcoverage_trace = None
122
+
123
+
124
+ @contextmanager
125
+ def xsltcoverage(output=None):
126
+ xsltcoverage = XSLTCoverage()
127
+ globals()['xsltcoverage_trace'] = xsltcoverage
128
+ yield xsltcoverage
129
+ globals()['xsltcoverage_trace'] = None
130
+ if output is not None:
131
+ if isinstance(output, basestring):
132
+ with open(output, 'w') as f:
133
+ xsltcoverage.writeto(f)
134
+ elif hasattr(output, 'write'):
135
+ xsltcoverage.writeto(output)
136
+
137
+
138
+ def instrument_xalan_transformer_factory(transformfac):
139
+ from javax.xml.transform import Templates
140
+ try:
141
+ from org.apache.xalan.processor import TransformerFactoryImpl
142
+ from org.apache.xalan.transformer import TransformerImpl
143
+ from org.apache.xalan.trace import TraceListenerEx2
144
+ except ImportError:
145
+ logger.warning('Xalan-J is not found: '
146
+ 'check your CLASSPATH. '
147
+ 'there will be no coverage data')
148
+ return transformfac
149
+ if not isinstance(transformfac, TransformerFactoryImpl):
150
+ logger.warning('TransformerFactory implementation is not Xalan-J: '
151
+ 'check system property of '
152
+ 'javax.xml.transform.TransformerFactory. '
153
+ 'there will be no coverage data')
154
+ return transformfac
155
+ if xsltcoverage_trace is None:
156
+ return transformfac
157
+
158
+ class XalanTraceListener(TraceListenerEx2):
159
+
160
+ def trace(self, ev):
161
+ stylenode = ev.m_styleNode
162
+ systemId = stylenode.getSystemId()
163
+ startline = stylenode.getLineNumber()
164
+ endline = stylenode.getEndLineNumber()
165
+
166
+ xsltcoverage_trace.trace(systemId, startline, endline)
167
+
168
+ traceListener = XalanTraceListener()
169
+
170
+ class XalanTransformerInstrumentImpl(TransformerFactoryImpl):
171
+
172
+ def newTemplates(self, source):
173
+ templates = TransformerFactoryImpl.newTemplates(self, source)
174
+ return XalanTemplates(templates, source)
175
+
176
+ def newTransformer(self, source=None):
177
+ impl = TransformerFactoryImpl.newTransformer(self, source)
178
+ # add listener
179
+ tracemanager = impl.getTraceManager()
180
+ tracemanager.addTraceListener(traceListener)
181
+ return impl
182
+
183
+ class XalanTemplates(Templates):
184
+
185
+ def __init__(self, t, s):
186
+ self.templates = t
187
+ self.source = s
188
+
189
+ def newTransformer(self):
190
+ transformer = self.templates.newTransformer()
191
+ # add listener
192
+ tracemanager = transformer.getTraceManager()
193
+ tracemanager.addTraceListener(traceListener)
194
+ return transformer
195
+
196
+ def getOutputProperties(self):
197
+ return self.templates.getOutputProperties()
198
+
199
+ return XalanTransformerInstrumentImpl()
200
+
201
+
202
+ XML_URI = 'http://www.w3.org/XML/1998/namespace'
203
+ XMLNS_URI = 'http://www.w3.org/2000/xmlns/'
204
+ TRANSIENT_NAMESPACE_PREFIXES = {'xml': XML_URI, 'xmlns': XMLNS_URI}
205
+
206
+
207
+ def split_prefix_and_localName(tag):
208
+ if ':' in tag:
209
+ return tag.split(':', 1)
210
+ else:
211
+ return None, tag
212
+
213
+
214
+ def split_jcnotation(tag):
215
+ ''' Get namespaceURI and localName from a James Clark's notation.
216
+
217
+ See http://www.jclark.com/xml/xmlns.htm
218
+ '''
219
+ if tag[0] != '{':
220
+ return None, tag
221
+ pos = tag.find('}')
222
+ if pos == -1:
223
+ raise ValueError(tag)
224
+ return tag[1:pos], tag[pos+1:]
225
+
226
+
227
+ def get_prefix_for_uri(namespaceURI, nsmap):
228
+ for prefix, uri in nsmap.items():
229
+ if uri == namespaceURI:
230
+ return prefix
231
+ if namespaceURI in TRANSIENT_NAMESPACE_PREFIXES:
232
+ return TRANSIENT_NAMESPACE_PREFIXES[namespaceURI]
233
+ prefix = 'ns' + str(len(TRANSIENT_NAMESPACE_PREFIXES))
234
+ TRANSIENT_NAMESPACE_PREFIXES[namespaceURI] = prefix
235
+ return prefix
236
+
237
+
238
+ def dom_element_from_tag(tag, nsmap=None):
239
+ if nsmap is None:
240
+ nsmap = {}
241
+ doc = builder.newDocument()
242
+ namespaceURI, localName = split_jcnotation(tag)
243
+ if namespaceURI is not None:
244
+ prefix = get_prefix_for_uri(namespaceURI, nsmap)
245
+ return doc.createElementNS(namespaceURI, prefix + ':' + localName)
246
+ else:
247
+ return doc.createElement(localName)
248
+
249
+
250
+ def dom_document_from_file(f):
251
+ if hasattr(f, 'name'):
252
+ documentURI = os.path.abspath(f.name)
253
+ else:
254
+ documentURI = None
255
+ bytestring = f.read()
256
+
257
+ temp = File.createTempFile('someprefix', 'tmp')
258
+
259
+ outputstream = FileOutputStream(temp)
260
+ try:
261
+ outputstream.write(bytestring)
262
+ finally:
263
+ outputstream.close()
264
+
265
+ inputstream = FileInputStream(temp)
266
+ try:
267
+ dom_doc = dom_document_from_inputstream(inputstream)
268
+ dom_doc.setDocumentURI(documentURI)
269
+ return dom_doc
270
+ finally:
271
+ inputstream.close()
272
+
273
+
274
+ def dom_document_from_inputstream(inputstream):
275
+ return builder.parse(inputstream)
276
+
277
+
278
+ def dom_tag(dom_element):
279
+ return dom_jcnotation(dom_element)
280
+
281
+
282
+ def dom_jcnotation(dom_node):
283
+ ''' Get node name expressed in James Clark's notation.
284
+
285
+ See http://www.jclark.com/xml/xmlns.htm
286
+ '''
287
+ namespaceURI = dom_node.namespaceURI
288
+ if namespaceURI:
289
+ namespace = '{%s}' % namespaceURI
290
+ return namespace + dom_node.localName
291
+ else:
292
+ return dom_node.nodeName
293
+
294
+
295
+ def dom_nsmap_specified_here(dom_element):
296
+ nsmap = {}
297
+ attrs = dom_element.attributes
298
+ for i in range(0, attrs.length):
299
+ attr = attrs.item(i)
300
+ if attr.name.startswith('xmlns:'):
301
+ prefix = attr.name[len('xmlns:'):]
302
+ uri = attr.value
303
+ nsmap[prefix] = uri
304
+ else:
305
+ prefix = attr.prefix
306
+ uri = attr.namespaceURI
307
+ if prefix and uri:
308
+ nsmap[prefix] = uri
309
+ return nsmap
310
+
311
+
312
+ dom_node_is_element = lambda n: n.nodeType == dom.Node.ELEMENT_NODE
313
+
314
+
315
+ def dom_ancestors(dom_node):
316
+ while True:
317
+ dom_node = dom_node.parentNode
318
+ if dom_node:
319
+ yield dom_node
320
+ else:
321
+ return
322
+
323
+
324
+ def dom_ancestor_elements(dom_node):
325
+ return takewhile(dom_node_is_element,
326
+ dom_ancestors(dom_node))
327
+
328
+
329
+ def dom_siblings_next(dom_node):
330
+ while True:
331
+ dom_node = dom_node.nextSibling
332
+ if dom_node:
333
+ yield dom_node
334
+ else:
335
+ return
336
+
337
+
338
+ def dom_siblings_previous(dom_node):
339
+ while True:
340
+ dom_node = dom_node.previousSibling
341
+ if dom_node:
342
+ yield dom_node
343
+ else:
344
+ return
345
+
346
+
347
+ def dom_nsmap(dom_element):
348
+ assert dom_element.nodeType == dom_element.ELEMENT_NODE
349
+ default_nsmap = dom_nsmap_specified_here(dom_element)
350
+ for ancestor in dom_ancestor_elements(dom_element):
351
+ nsmap = dom_nsmap_specified_here(ancestor)
352
+ nsmap.update(default_nsmap)
353
+ default_nsmap = nsmap
354
+ return default_nsmap
355
+
356
+
357
+ def dom_set_attr(dom_element, key, value, nsmap):
358
+ namespaceURI, localName = split_jcnotation(key)
359
+ if namespaceURI:
360
+ prefix = get_prefix_for_uri(namespaceURI, nsmap)
361
+ qualifiedName = prefix + ':' + localName
362
+ dom_element.setAttributeNS(namespaceURI, qualifiedName, value)
363
+ else:
364
+ dom_element.setAttribute(key, value)
365
+
366
+
367
+ def Element(tag, attrib=None, nsmap=None, **extra):
368
+ assert isinstance(tag, basestring)
369
+ dom_element = dom_element_from_tag(tag, nsmap)
370
+ nsmap = nsmap or {}
371
+
372
+ dom_document = dom_element.getOwnerDocument()
373
+ for prefix, namespaceURI in nsmap.items():
374
+ dom_element.setAttributeNS(XMLNS_URI, 'xmlns:'+prefix, namespaceURI)
375
+
376
+ if attrib:
377
+ extra.update(attrib)
378
+ if extra:
379
+ for k, v in extra.items():
380
+ dom_set_attr(dom_element, k, v, nsmap)
381
+ return _Element(dom_element)
382
+
383
+
384
+ def SubElement(parent, tag, attrib=None, nsmap=None, **extra):
385
+ element = Element(tag, attrib, nsmap, **extra)
386
+ parent.append(element)
387
+ return element
388
+
389
+
390
+ def ElementTree(element=None, file=None, parser=None):
391
+ if parser is not None:
392
+ raise NotImplementedError('parser is not supported')
393
+ elif element is not None:
394
+ dom_doc = builder.newDocument()
395
+ dom_doc.documentURI = element._dom_element.ownerDocument.documentURI
396
+ dom_root = dom_doc.importNode(element._dom_element, True)
397
+ dom_doc.appendChild(dom_root)
398
+ _Element(dom_root)
399
+ elif file is not None:
400
+ dom_doc = dom_document_from_file(file)
401
+ else:
402
+ raise ValueError('element and file is None')
403
+ return _ElementTree(dom_doc)
404
+
405
+
406
+ class _Element(object):
407
+
408
+ def __init__(self, dom_element):
409
+ if dom_element.nodeType != dom_element.ELEMENT_NODE:
410
+ raise ValueError('ELEMENT_NODE(%d) required: %d' %
411
+ (dom_element.ELEMENT_NODE, dom_element.nodeType))
412
+ self._dom_element = dom_element
413
+
414
+ def getroottree(self):
415
+ dom_doc = self._dom_element.getOwnerDocument()
416
+ return _ElementTree(dom_doc)
417
+
418
+ @property
419
+ def attrib(self):
420
+ attrib = dict()
421
+ dom_attrib = self._dom_element.getAttributes()
422
+ for i in range(dom_attrib.length):
423
+ child = dom_attrib.item(i)
424
+ name = child.name
425
+ if not name.startswith('xmlns:') and name != 'xmlns':
426
+ attrib[dom_jcnotation(child)] = child.value
427
+ return attrib
428
+
429
+ @property
430
+ def tag(self):
431
+ return dom_tag(self._dom_element)
432
+
433
+ @property
434
+ def nsmap(self):
435
+ return dom_nsmap(self._dom_element)
436
+
437
+ @property
438
+ def prefix(self):
439
+ namespaceURI = self._dom_element.namespaceURI
440
+ return self._dom_element.lookupPrefix(namespaceURI)
441
+
442
+ @property
443
+ def text(self):
444
+ node = self._dom_element.firstChild
445
+ if node and node.nodeType == node.TEXT_NODE:
446
+ return node.getWholeText()
447
+
448
+ @property
449
+ def tail(self):
450
+ node = self._dom_element.nextSibling
451
+ if node and node.nodeType == node.TEXT_NODE:
452
+ return node.getWholeText()
453
+
454
+ @property
455
+ def base(self):
456
+ dom_doc = self._dom_element.ownerDocument
457
+ return dom_doc.documentURI
458
+
459
+ def __deepcopy__(self, memo):
460
+ dom_doc = builder.newDocument()
461
+ copied = dom_doc.importNode(self._dom_element, True)
462
+ return _Element(copied)
463
+
464
+ def __iter__(self):
465
+ childs = self._dom_element.getChildNodes()
466
+ for i in range(childs.length):
467
+ child = childs.item(i)
468
+ if child.nodeType == child.ELEMENT_NODE:
469
+ yield _Element(child)
470
+
471
+ def __getitem__(self, index):
472
+ if index >= 0:
473
+ iterable = self.__iter__()
474
+ else:
475
+ iterable = self.__reversed__()
476
+ index = -index - 1
477
+ for child in iterable:
478
+ if index == 0:
479
+ return child
480
+ index -= 1
481
+ raise IndexError(index)
482
+
483
+ def __len__(self):
484
+ n = 0
485
+ for _ in self.__iter__():
486
+ n += 1
487
+ return n
488
+
489
+ def __reversed__(self):
490
+ childs = self._dom_element.getChildNodes()
491
+ length = childs.length
492
+ for x in range(length):
493
+ i = length - x - 1
494
+ child = childs.item(i)
495
+ if child.nodeType == child.ELEMENT_NODE:
496
+ yield _Element(child)
497
+
498
+ def __setitem__(self, index, new_elem):
499
+ old_elem = self.__getitem__(index)
500
+
501
+ dom_document = self._dom_element.getOwnerDocument()
502
+ dom_element_new = dom_document.importNode(new_elem._dom_element, True)
503
+ self._dom_element.replaceChild(dom_element_new,
504
+ old_elem._dom_element)
505
+
506
+ def append(self, element):
507
+ dom_document = self._dom_element.getOwnerDocument()
508
+ dom_element_new = dom_document.importNode(element._dom_element, True)
509
+ self._dom_element.appendChild(dom_element_new)
510
+ element._dom_element = dom_element_new
511
+
512
+ def get(self, key):
513
+ return self.attrib.get(key)
514
+
515
+ def getchildren(self):
516
+ # TODO: deprecated warning
517
+ return list(self.__iter__())
518
+
519
+ def getnext(self):
520
+ sibling = None
521
+ for sibling in dom_siblings_next(self._dom_element):
522
+ if dom_node_is_element(sibling):
523
+ return _Element(sibling)
524
+
525
+ def getparent(self):
526
+ dom_parent = self._dom_element.getParentNode()
527
+ if dom_parent and dom_node_is_element(dom_parent):
528
+ return _Element(dom_parent)
529
+
530
+ def getprevious(self):
531
+ sibling = None
532
+ for sibling in dom_siblings_previous(self._dom_element):
533
+ if dom_node_is_element(sibling):
534
+ return _Element(sibling)
535
+
536
+ def iterancestors(self, *tags):
537
+ parents = imap(_Element, dom_ancestor_elements(self._dom_element))
538
+ if tags:
539
+ parents = ifilter(lambda elem: elem.tag in tags,
540
+ parents)
541
+ return parents
542
+
543
+ def keys(self):
544
+ return self.attrib.keys()
545
+
546
+ def set(self, key, value):
547
+ dom_doc = self._dom_element.getOwnerDocument()
548
+ dom_element = self._dom_element
549
+ dom_set_attr(dom_element, key, value, self.nsmap)
550
+
551
+ def values(self):
552
+ return self.attrib.values()
553
+
554
+ def xpath(self, _path, namespaces=None, extensions=None,
555
+ smart_strings=True, **_variables):
556
+ xpath = XPath(_path, namespaces, extensions, smart_strings)
557
+ return xpath(self, **_variables)
558
+
559
+
560
+ class _ElementTree(object):
561
+
562
+ def __init__(self, dom_doc):
563
+ self._set_dom_doc(dom_doc)
564
+
565
+ def __copy__(self, dom_doc):
566
+ raise NotImplementedError('__copy__() is not implemented yet')
567
+
568
+ def __deepcopy__(self, memo):
569
+ dom_doc = builder.newDocument()
570
+ dom_root = dom_doc.importNode(self.getroot()._dom_element, True)
571
+ return _ElementTree(dom_doc)
572
+
573
+ @property
574
+ def docinfo(self):
575
+ dom_doc = self._dom_doc
576
+ return DocInfo(dom_doc)
577
+
578
+ def _setroot(self, root):
579
+ self._set_dom_doc(root._dom_element.getOwnerDocument())
580
+
581
+ def _set_dom_doc(self, dom_doc):
582
+ if dom_doc.nodeType != dom_doc.DOCUMENT_NODE:
583
+ raise ValueError('DOCUMENT_NODE(%d) required: %d' %
584
+ (dom.DOCUMENT_NODE, dom_doc.nodeType))
585
+ self._dom_doc = dom_doc
586
+
587
+ def getroot(self):
588
+ dom_root = self._dom_doc.getDocumentElement()
589
+ if dom_root is not None:
590
+ return _Element(dom_root)
591
+
592
+ def find(self, path, namespaces=None):
593
+ root = self.getroot()
594
+ return root.find(path, namespaces)
595
+
596
+ def findall(self, path, namespaces=None):
597
+ root = self.getroot()
598
+ return root.findall(path, namespaces)
599
+
600
+ def findtext(self, path, default=None, namespaces=None):
601
+ root = self.getroot()
602
+ return root.findtext(path, default, namespaces)
603
+
604
+ def iter(self, **tags):
605
+ root = self.getroot()
606
+ return root.iter(*tags)
607
+
608
+ def iterfind(self, path, namespaces=None):
609
+ root = self.getroot()
610
+ return root.iterfind(path)
611
+
612
+ def relaxng(self, relaxng):
613
+ raise NotImplementedError('relaxng is not implemented yet')
614
+
615
+ def write(self, file, encoding=None, method='xml', pretty_print=False,
616
+ xml_declaration=None, with_tail=True, standalone=None,
617
+ compression=0, exclusive=False, with_comments=True,
618
+ inclusive_ns_prefixes=None):
619
+
620
+ if compression != 0:
621
+ raise NotImplementedError('compression should be 0')
622
+
623
+ s = tostring(self, encoding=encoding, method=method,
624
+ pretty_print=pretty_print,
625
+ xml_declaration=xml_declaration, with_tail=with_tail,
626
+ standalone=standalone, exclusive=exclusive,
627
+ with_comments=with_comments,
628
+ inclusive_ns_prefixes=inclusive_ns_prefixes)
629
+ if isinstance(file, basestring):
630
+ with open(file, 'w') as f:
631
+ f.write(s)
632
+ elif hasattr(file, 'write'):
633
+ file.write(s)
634
+
635
+ def write_c14n(self, file, exlusive=False, with_comments=True,
636
+ compression=0, inclusive_ns_prefixes=None):
637
+ raise NotImplementedError('write_c14n() is not implemented yet')
638
+
639
+ def xinclude(self):
640
+ raise NotImplementedError('xinclude() is not implemented yet')
641
+
642
+ def xmlschema(self, xmlschema):
643
+ raise NotImplementedError('xmlschema() is not implemented yet')
644
+
645
+ def xpath(self, _path, namespaces=None, extensions=None,
646
+ smart_strings=True, **_variables):
647
+ xpath = XPath(_path, namespaces, extensions, smart_strings)
648
+ return xpath(self, **_variables)
649
+
650
+ def xslt(self, _xslt, extensions=None, access_control=None, **_kw):
651
+ xslt = XSLT(_xslt, extensions=extensions,
652
+ access_control=access_control)
653
+ return xslt(self, **_kw)
654
+
655
+
656
+ class DocInfo(object):
657
+
658
+ def __init__(self, dom_doc):
659
+ self.dom_doc = dom_doc
660
+
661
+ def getURL(self):
662
+ return self.dom_doc.documentURI
663
+
664
+ def setURL(self, uri):
665
+ self.dom_doc.documentURI = uri
666
+
667
+ URL = property(getURL, setURL)
668
+
669
+ @property
670
+ def doctype(self):
671
+ doctype = self.dom_doc.doctype
672
+ if doctype:
673
+ return doctype.name
674
+ return ''
675
+
676
+ @property
677
+ def public_id(self):
678
+ doctype = self.dom_doc.doctype
679
+ if doctype:
680
+ return doctype.publicId
681
+
682
+ @property
683
+ def system_url(self):
684
+ doctype = self.dom_doc.doctype
685
+ if doctype:
686
+ return doctype.systemId
687
+
688
+ @property
689
+ def encoding(self):
690
+ return self.dom_doc.getXmlEncoding()
691
+
692
+ @property
693
+ def root_name(self):
694
+ dom_root = self.dom_doc.getDocumentElement()
695
+ if dom_root:
696
+ return dom_root.localName
697
+
698
+ @property
699
+ def standalone(self):
700
+ return self.dom_doc.getXmlStandalone()
701
+
702
+ @property
703
+ def xml_version(self):
704
+ return self.dom_doc.getXmlVersion()
705
+
706
+ @property
707
+ def externalDTD(self):
708
+ # TODO
709
+ return None
710
+
711
+ @property
712
+ def internalDTD(self):
713
+ # TODO
714
+ return None
715
+
716
+
717
+ class QName(object):
718
+
719
+ def __init__(self, text_or_uri_or_element, tag=None):
720
+ x = text_or_uri_or_element
721
+ if tag is not None:
722
+ if not isinstance(x, basestring):
723
+ raise ValueError('Bad namespace URI: %s' %
724
+ text_or_uri_or_element)
725
+ if not isinstance(tag, basestring):
726
+ raise ValueError('Bad tag: %s', tag)
727
+ self.namespace = x
728
+ self.localname = tag
729
+ elif isinstance(x, _Element):
730
+ assert tag is None
731
+ dom_node = x._dom_element
732
+ self.namespace = dom_node.namespaceURI
733
+ self.localname = dom_node.localName
734
+ elif isinstance(x, basestring):
735
+ self.namespace, self.localname = split_jcnotation(x)
736
+ else:
737
+ raise ValueError('Bad argument: %s' % text_or_uri_or_element)
738
+
739
+ @property
740
+ def text(self):
741
+ if self.namespace:
742
+ return '{%s}%s' % (self.namespace, self.localname)
743
+ return self.localname
744
+
745
+
746
+ class XPath(object):
747
+
748
+ def __init__(self, path, namespaces=None, extensions=None, regexp=True,
749
+ smart_strings=True):
750
+ if extensions:
751
+ raise NotImplementedError('extensions are not supported')
752
+
753
+ xpath = xpathfac.newXPath()
754
+ nscontext = NamespaceContextImpl(namespaces or {})
755
+ xpath.setNamespaceContext(nscontext)
756
+ self.xpathexpr = xpath.compile(path)
757
+
758
+ def __call__(self, _etree_or_element, **_variables):
759
+ if len(_variables):
760
+ raise NotImplementedError('variables are not supported')
761
+
762
+ if isinstance(_etree_or_element, _ElementTree):
763
+ node = _etree_or_element._dom_doc.getDocumentElement()
764
+ elif isinstance(_etree_or_element, _Element):
765
+ node = _etree_or_element._dom_element
766
+ else:
767
+ logger.error('_etree_or_element should be an instance of '
768
+ '_Element/_ElementTree')
769
+ raise ValueError()
770
+
771
+ # TODO: heuristic
772
+ try:
773
+ return self._eval_nodeset(node)
774
+ except XPathExpressionException, e:
775
+ pass
776
+
777
+ try:
778
+ return self._eval_node(node)
779
+ except XPathExpressionException, e:
780
+ pass
781
+
782
+ try:
783
+ result = self._eval_number(node)
784
+ if not (isinstance(result, float) and str(result) == 'nan'):
785
+ return result
786
+ except XPathExpressionException, e:
787
+ pass
788
+
789
+ try:
790
+ return self._eval_string(node)
791
+ except XPathExpressionException, e:
792
+ pass
793
+
794
+ return self._eval_boolean(node)
795
+
796
+ def _eval_nodeset(self, node):
797
+ result = self.xpathexpr.evaluate(node, XPathConstants.NODESET)
798
+ return dom_xpath_result(result)
799
+
800
+ def _eval_node(self, node):
801
+ result = self.xpathexpr.evaluate(node, XPathConstants.NODE)
802
+ return dom_xpath_result(result)
803
+
804
+ def _eval_string(self, node):
805
+ result = self.xpathexpr.evaluate(node, XPathConstants.STRING)
806
+ return dom_xpath_result(result)
807
+
808
+ def _eval_number(self, node):
809
+ result = self.xpathexpr.evaluate(node, XPathConstants.NUMBER)
810
+ return dom_xpath_result(result)
811
+
812
+ def _eval_boolean(self, node):
813
+ result = self.xpathexpr.evaluate(node, XPathConstants.BOOLEAN)
814
+ return dom_xpath_result(result)
815
+
816
+
817
+ @simplegeneric
818
+ def dom_xpath_result(result):
819
+ return result
820
+
821
+
822
+ @dom_xpath_result.register(dom.NodeList)
823
+ def dom_xpath_result_nodelist(nodelist):
824
+ resultset = []
825
+ for i in range(nodelist.length):
826
+ item = nodelist.item(i)
827
+ resultset.append(dom_xpath_result(item))
828
+ return resultset
829
+
830
+
831
+ @dom_xpath_result.register(dom.Element)
832
+ def dom_xpath_result_element(element):
833
+ return _Element(element)
834
+
835
+
836
+ @dom_xpath_result.register(dom.Attr)
837
+ def dom_xpath_result_attr(attr):
838
+ return attr.value
839
+
840
+
841
+ class NamespaceContextImpl(NamespaceContext):
842
+
843
+ def __init__(self, nsmap):
844
+ self.nsmap = nsmap
845
+
846
+ def getNamespaceURI(self, prefix):
847
+ # TODO: default namespace, etc., not implemented
848
+ if prefix is None:
849
+ raise IllegalArgumentException()
850
+ if prefix in self.nsmap:
851
+ return self.nsmap[prefix]
852
+ return XMLConstants.NULL_NS_URI
853
+
854
+ def getPrefixes(self, namespaceURI):
855
+ if namespaceURI is None:
856
+ raise IllegalArgumentException()
857
+ return IteratorImpl(self.iterPrefixes(namespaceURI))
858
+
859
+ def getPrefix(self, namespaceURI):
860
+ prefixes = self.getPrefixes(namespaceURI)
861
+ if prefixes is None:
862
+ return None
863
+
864
+ def iterPrefixes(self, namespaceURI):
865
+ if namespaceURI is None:
866
+ raise IllegalArgumentException()
867
+ for prefix, uri in self.nsmap.items():
868
+ if uri == namespaceURI:
869
+ yield prefix
870
+
871
+
872
+ class IteratorImpl(Iterator):
873
+
874
+ def __init__(self, it):
875
+ self.it = it
876
+ self._goNext()
877
+
878
+ def hasNext(self):
879
+ return self._hasNext
880
+
881
+ def next(self):
882
+ if self._hasNext:
883
+ try:
884
+ return self._nextItem
885
+ finally:
886
+ self._goNext()
887
+ else:
888
+ raise NoSuchElementException()
889
+
890
+ def _goNext(self):
891
+ try:
892
+ self._nextItem = it.next()
893
+ self._hasNext = True
894
+ except StopIteration:
895
+ self._nextItem = None
896
+ self._hasNext = False
897
+
898
+
899
+ class XSLT(object):
900
+
901
+ def __init__(self, xsl_input, extensions=None, regexp=True,
902
+ access_control=None):
903
+ if extensions:
904
+ raise NotImplementedError('extensions is not supported')
905
+ if access_control:
906
+ raise NotImplementedError('access_control is not supported')
907
+
908
+ if isinstance(xsl_input, _ElementTree):
909
+ xsl_tree = xsl_input
910
+ elif isinstance(xsl_input, _Element):
911
+ xsl_tree = ElementTree(xsl_input)
912
+ else:
913
+ raise ValueError(xsl_input)
914
+
915
+ self.xsl_tree = xsl_tree
916
+ self.xsl_source = DOMSource(xsl_tree._dom_doc, xsl_tree.docinfo.URL)
917
+ self.uri_resolver = __URIResolverImpl(xsl_tree)
918
+
919
+ #print tostring(xsl_tree)
920
+ fac = TransformerFactory.newInstance()
921
+ fac.setURIResolver(self.uri_resolver)
922
+ fac = instrument_xalan_transformer_factory(fac)
923
+ self.transformer = fac.newTransformer(self.xsl_source)
924
+
925
+ def __call__(self, _input, profile_run=False, **kw):
926
+
927
+ nsmap = dict(xsl='http://www.w3.org/1999/XSL/Transform')
928
+ output_method = 'xml'
929
+ output_encoding = 'utf-8'
930
+
931
+ nodes = self.xsl_tree.xpath('xsl:output/@method', namespaces=nsmap)
932
+ if len(nodes) > 0:
933
+ output_method = nodes[0]
934
+
935
+ nodes = self.xsl_tree.xpath('xsl:output/@encoding', namespaces=nsmap)
936
+ if len(nodes) > 0:
937
+ output_encoding = nodes[0]
938
+
939
+ #print tostring(_input)
940
+ if isinstance(_input, _ElementTree):
941
+ doc_source = DOMSource(_input._dom_doc)
942
+ elif isinstance(_input, _Element):
943
+ # Xalan-J 2.7.1 does not support a DOMSource from an Element
944
+ # so we build new document
945
+ dom_doc = builder.newDocument()
946
+ dom_root = dom_doc.importNode(_input._dom_element, True)
947
+ dom_doc.appendChild(dom_root)
948
+ doc_source = DOMSource(dom_doc)
949
+ else:
950
+ raise NotImplementedError()
951
+
952
+ if output_method in ('xml', 'html'):
953
+
954
+ # TODO: for testing
955
+ outputstream = ByteArrayOutputStream()
956
+ result = StreamResult(outputstream)
957
+ self.transformer.transform(doc_source, result)
958
+ bytes = outputstream.toByteArray()
959
+ inputstream = ByteArrayInputStream(bytes)
960
+ try:
961
+ dom_doc = builder.parse(inputstream)
962
+ except:
963
+ import sys
964
+ sys.stderr.write(bytes.tostring())
965
+ raise
966
+ result_tree = _ElementTree(dom_doc)
967
+ return result_tree
968
+
969
+ result = DOMResult()
970
+ self.transformer.transform(doc_source, result)
971
+ dom_doc = result.getNode()
972
+ result_tree = _ElementTree(dom_doc)
973
+ #print tostring(result_tree)
974
+ return result_tree
975
+ else:
976
+ outputstream = ByteArrayOutputStream()
977
+ result = StreamResult(outputstream)
978
+ self.transformer.transform(doc_source, result)
979
+
980
+ resultdoc = builder.newDocument()
981
+ resulttree = _XSLTResultTree(resultdoc)
982
+ resulttree._text = outputstream.toString(output_encoding)
983
+ return resulttree
984
+
985
+
986
+ class _XSLTResultTree(_ElementTree):
987
+
988
+ def __unicode__(self):
989
+ return self._text
990
+
991
+ def __str__(self):
992
+ return self._text.encode(self.docinfo.encoding)
993
+
994
+
995
+ class __URIResolverImpl(URIResolver):
996
+
997
+ def __init__(self, tree):
998
+ self.__logger = logging.getLogger(__name__+'.'+type(self).__name__)
999
+ self.base = tree.docinfo.URL
1000
+
1001
+ def resolve(self, href, base):
1002
+ self.__logger.debug('default_base: %r', self.base)
1003
+ self.__logger.debug('href: %r', href)
1004
+ self.__logger.debug('base: %r', base)
1005
+ if base is None:
1006
+ base = self.base
1007
+
1008
+ if base is not None:
1009
+ href = urljoin(base, href)
1010
+ else:
1011
+ href = href
1012
+
1013
+ href = urlparse(href)
1014
+
1015
+ # if href is not a URL
1016
+ if not href.scheme and not href.hostname:
1017
+ return StreamSource(File(href.path))
1018
+
1019
+
1020
+ def parse(source, parser=None, base_url=None):
1021
+ if base_url is not None:
1022
+ raise NotImplementedError('base_url is not supported')
1023
+
1024
+ if isinstance(source, basestring):
1025
+ with open(source) as f:
1026
+ return ElementTree(file=f)
1027
+ if hasattr(source, 'read'):
1028
+ return ElementTree(file=source)
1029
+ assert False, 'source should be a string or file-like object'
1030
+
1031
+
1032
+ def fromstring(text, parser=None, base_url=None):
1033
+ if base_url is not None:
1034
+ raise NotImplementedError('base_url is not supported')
1035
+ from StringIO import StringIO
1036
+ f = StringIO(text)
1037
+ return ElementTree(file=f)
1038
+
1039
+
1040
+ def tostring(element_or_tree, encoding=None, method='xml',
1041
+ xml_declaration=None, pretty_print=False, with_tail=True,
1042
+ standalone=None, doctype=None, exclusive=False,
1043
+ with_comments=True, inclusive_ns_prefixes=None):
1044
+ if isinstance(element_or_tree, _ElementTree):
1045
+ source = DOMSource(element_or_tree._dom_doc)
1046
+ else:
1047
+ source = DOMSource(element_or_tree._dom_element)
1048
+
1049
+ outputstream = ByteArrayOutputStream()
1050
+ result = StreamResult(outputstream)
1051
+ transformer = transformfac.newTransformer()
1052
+ if xml_declaration:
1053
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
1054
+ 'no')
1055
+ else:
1056
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
1057
+ 'yes')
1058
+ if pretty_print:
1059
+ transformer.setOutputProperty(OutputKeys.INDENT, 'yes')
1060
+ else:
1061
+ transformer.setOutputProperty(OutputKeys.INDENT, 'no')
1062
+ transformer.transform(source, result)
1063
+
1064
+ if encoding is None:
1065
+ encoding = 'ascii'
1066
+ return outputstream.toString(encoding)
1067
+
1068
+
1069
+ def XML(s):
1070
+ from StringIO import StringIO
1071
+ sio = StringIO(s)
1072
+ return parse(sio).getroot()
tools/jxml/lxml/lxml/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from jxml import *
tools/jxml/lxml/lxml/etree/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from jxml.etree import *
tools/jxml/lxml/setup.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from setuptools import setup, find_packages
3
+ import os.path
4
+ import sys
5
+ if not sys.platform.startswith('java'):
6
+ raise SystemExit(1)
7
+ os.chdir(os.path.dirname(__file__))
8
+ setup(name='jxml.lxml',
9
+ packages=find_packages())
tools/jxml/setup.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from setuptools import setup, find_packages
3
+ import os.path
4
+ import sys
5
+ if not sys.platform.startswith('java'):
6
+ raise SystemExit(1)
7
+ os.chdir(os.path.dirname(__file__))
8
+ setup(name='jxml',
9
+ packages=find_packages())
tools/jxml/tests/Makefile ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BUILDOUT_DIR := ${realpath ../../..}
2
+ JXML_DIR := ${realpath ..}
3
+ JXML_BIN_DIR:=$(JXML_DIR)/bin
4
+ JXML_LXML_DIR := ${JXML_DIR}/lxml
5
+ TEST_XSL_DIR := ${JXML_DIR}/tests/test-xsl
6
+ XML_FIXT_DIR := ${TEST_XSL_DIR}/fxt
7
+ TEST_GEN_DIR := ${TEST_XSL_DIR}/gen
8
+
9
+
10
+
11
+ XALAN_J_ARCHIVE = ${BUILDOUT_DIR}/parts/xalan-j_2_7_1-bin-2jars.tar.gz
12
+ XALAN_J_DIR = ${BUILDOUT_DIR}/parts/xalan-j_2_7_1
13
+ XALAN_J_CLASSPATH = ${XALAN_J_DIR}/xalan.jar:${XALAN_J_DIR}/xercesImpl.jar:${XALAN_J_DIR}/serializer.jar:${XALAN_J_DIR}/xml-apis.jar
14
+ XALAN_J_SYS_PROPS = -Djavax.xml.transform.TransformerFactory=org.apache.xalan.processor.TransformerFactoryImpl -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl
15
+ XALAN_J_ENVSET = CLASSPATH=${XALAN_J_CLASSPATH}
16
+ XALAN_J_JYTHON = ${XALAN_J_ENVSET} ${JXML_BIN_DIR}/jython ${XALAN_J_SYS_PROPS}
17
+
18
+
19
+ DEFAULT_JYTHON = ${JXML_BIN_DIR}/jython
20
+ JXML_ENVSET = JYTHONPATH=${JXML_DIR}:${JXML_LXML_DIR}
21
+
22
+ .PHONY: test-jaxp test-jaxp-xalan test-python test-jython test-jython-xalan test-xsl test-xsl-xalan test-jxml-cov-test test-jxml-annotate
23
+ all: test-jaxp test-jaxp-xalan test-python test-jython test-jython-xalan test-xsl test-xsl-xalan test-jxml-cov-test test-jxml-annotate
24
+
25
+ test-python:
26
+ python test_lxml.py
27
+ test-jython:
28
+ ${JXML_ENVSET} ${DEFAULT_JYTHON} test_lxml.py
29
+ test-jython-xalan: ${XALAN_J_DIR}
30
+ #${XALAN_J_JYTHON} test_textout.py
31
+ ${JXML_ENVSET} ${XALAN_J_JYTHON} test_lxml.py
32
+ test-xsl:
33
+ for fn in `ls ${BUILDOUT_DIR}/test-xsl/gen/*.py`; do echo $$fn; ${JXML_ENVSET} ${DEFAULT_JYTHON} $$fn; done
34
+ test-xsl-xalan: ${XALAN_J_DIR}
35
+ for fn in `ls ${BUILDOUT_DIR}/test-xsl/gen/*.py`; do echo $$fn; ${JXML_ENVSET} ${XALAN_J_JYTHON} $$fn; done
36
+ test-jaxp:
37
+ ${DEFAULT_JYTHON} test_jaxp.py
38
+ test-jaxp-xalan:
39
+ ${XALAN_J_JYTHON} test_jaxp.py
40
+
41
+ test-jxml-cov-test:
42
+ ${XALAN_J_ENVSET} ${JXML_BIN_DIR}/jxml-cov-test coverage.xml ${BUILDOUT_DIR}/test-xsl/gen/*.py
43
+ test-jxml-annotate: test-jxml-cov-test
44
+ ${JXML_BIN_DIR}/jxml-annotate --color=yes coverage.xml > coverage.color.txt
45
+
46
+
47
+
48
+ ${XALAN_J_DIR}: ${XALAN_J_ARCHIVE}
49
+ ${BUILDOUT_DIR}/bin/pyhwp-unpack $< $@
50
+
51
+ ${XALAN_J_ARCHIVE}:
52
+ ${BUILDOUT_DIR}/bin/pyhwp-download http://mirror.apache-kr.org/xalan/xalan-j/binaries/xalan-j_2_7_1-bin-2jars.tar.gz ${BUILDOUT_DIR}/parts 3ccda39bcd08b780436dfd2f22fb23d5
tools/jxml/tests/hello.xml ADDED
@@ -0,0 +1 @@
 
 
1
+ <?xml version="1.0"?><hello />
tools/jxml/tests/sample.xml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <x:document xmlns:x="http://example.tld" version="1.0">
3
+ <x:paragraph>text<x:span>foo</x:span></x:paragraph>tail<z:object xmlns:z="http://z.example.tld" a="1" x:b="2" z:c="3" />
4
+ <x:third />
5
+ </x:document>
tools/jxml/tests/test_jaxp.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import unittest
4
+
5
+ from java.lang import System
6
+ from java.io import File
7
+ from java.io import ByteArrayOutputStream
8
+ from javax.xml.parsers import DocumentBuilderFactory
9
+ from javax.xml.transform import TransformerFactory
10
+ from javax.xml.transform.dom import DOMSource
11
+ from javax.xml.transform.stream import StreamSource
12
+ from javax.xml.transform.stream import StreamResult
13
+
14
+ dbfac = DocumentBuilderFactory.newInstance()
15
+ dbfac.namespaceAware = True
16
+ docfac = dbfac.newDocumentBuilder()
17
+ print type(dbfac)
18
+
19
+ transfac = TransformerFactory.newInstance()
20
+
21
+ src_dom = docfac.parse('hello.xml')
22
+ src_source = DOMSource(src_dom)
23
+
24
+
25
+ def unsigned_byte(x):
26
+ if x < 0:
27
+ return 256 + x
28
+ return x
29
+
30
+
31
+ def Transformer(xsl_source):
32
+ transformer = transfac.newTransformer(xsl_source)
33
+ def transform(src_source):
34
+ outputstream = ByteArrayOutputStream()
35
+ dst_result = StreamResult(outputstream)
36
+ transformer.transform(src_source, dst_result)
37
+ return ''.join(chr(unsigned_byte(x)) for x in outputstream.toByteArray())
38
+ return transform
39
+
40
+
41
+ def transform(xsl_source, src_source):
42
+ transform = Transformer(xsl_source)
43
+ return transform(src_source)
44
+
45
+
46
+ class TestXSLT(unittest.TestCase):
47
+
48
+ xsl_path = 'xsl/import-test.xsl'
49
+
50
+ def test_xsl_dom(self):
51
+
52
+ xsl_dom = docfac.parse(self.xsl_path)
53
+ # DOMSource with System Id
54
+ xsl_source = DOMSource(xsl_dom, self.xsl_path)
55
+
56
+ result = transform(xsl_source, src_source)
57
+ #print result
58
+ self.assertTrue('world' in result)
59
+
60
+ def test_xsl_stream(self):
61
+ xsl_source = StreamSource(self.xsl_path)
62
+
63
+ result = transform(xsl_source, src_source)
64
+ #print result
65
+ self.assertTrue('world' in result)
66
+
67
+
68
+ if __name__ == '__main__':
69
+ unittest.main()
tools/jxml/tests/test_lxml.py ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import with_statement
3
+
4
+ import os.path
5
+ import sys
6
+ import unittest
7
+ from unittest import TestCase
8
+
9
+ from lxml import etree
10
+ from lxml.etree import ElementTree
11
+ from lxml.etree import Element
12
+ from lxml.etree import SubElement
13
+ from lxml.etree import QName
14
+
15
+
16
+ class ElementTreeTest(TestCase):
17
+
18
+ def test_etree_parse(self):
19
+ with open('sample.xml') as f:
20
+ et = etree.parse(f)
21
+ et = etree.parse('sample.xml')
22
+
23
+ def test_etree_fromstring(self):
24
+ with open('sample.xml') as f:
25
+ text = f.read()
26
+ et = etree.fromstring(text)
27
+
28
+ def test_etree_from_file(self):
29
+ with open('sample.xml') as f:
30
+ et = ElementTree(file=f)
31
+ root = et.getroot()
32
+ self.assertEqual('{http://example.tld}document', root.tag)
33
+ self.assertEqual('x', root.prefix)
34
+ self.assertTrue('x' in root.nsmap)
35
+
36
+ with open('hello.xml') as f:
37
+ et = ElementTree(file=f)
38
+ root = et.getroot()
39
+ self.assertEqual('hello', root.tag)
40
+ self.assertEqual(None, root.prefix)
41
+ self.assertEqual({}, root.nsmap)
42
+
43
+ def test_etree_tostring(self):
44
+ with open('sample.xml') as f:
45
+ et = etree.parse(f)
46
+ etree.tostring(et, encoding='utf-8', xml_declaration=True)
47
+ etree.tostring(et.getroot()[0], encoding='utf-8', xml_declaration=True)
48
+
49
+ def test_from_element(self):
50
+ elem = Element('document')
51
+ tree = ElementTree(elem)
52
+ self.assertEqual('document', tree.getroot().tag)
53
+
54
+ with open('sample.xml') as f:
55
+ et = ElementTree(file=f)
56
+ root = et.getroot()
57
+
58
+ tree = ElementTree(root)
59
+ self.assertEqual(root.base, tree.getroot().base)
60
+ self.assertEqual(et.docinfo.URL, tree.docinfo.URL)
61
+
62
+ def test_docinfo(self):
63
+ with open('sample.xml') as f:
64
+ et = etree.parse(f)
65
+ import os.path
66
+ self.assertEqual(os.path.abspath('sample.xml'), et.docinfo.URL)
67
+ self.assertEqual('', et.docinfo.doctype)
68
+ self.assertEqual('utf-8', et.docinfo.encoding)
69
+ self.assertEqual(None, et.docinfo.externalDTD)
70
+ self.assertEqual(None, et.docinfo.internalDTD)
71
+ self.assertEqual(None, et.docinfo.public_id)
72
+ self.assertEqual('document', et.docinfo.root_name)
73
+ self.assertFalse(et.docinfo.standalone)
74
+ self.assertEqual(None, et.docinfo.system_url)
75
+ self.assertEqual('1.0', et.docinfo.xml_version)
76
+
77
+ et.docinfo.URL = 'http://example.tld'
78
+ self.assertEqual('http://example.tld', et.docinfo.URL)
79
+
80
+ def test_parser(self):
81
+ pass
82
+
83
+ def test__copy__(self):
84
+ pass
85
+
86
+ def test__deepcopy__(self):
87
+ pass
88
+
89
+ def test_setroot(self):
90
+ from lxml.etree import XML
91
+ a = XML('<a />').getroottree()
92
+ b = XML('<b />').getroottree()
93
+ a._setroot(b.getroot())
94
+ self.assertEqual('b', a.getroot().tag)
95
+
96
+ def test_find(self):
97
+ pass
98
+
99
+ def test_findall(self):
100
+ pass
101
+
102
+ def test_findtext(self):
103
+ pass
104
+
105
+ def test_getiterator(self):
106
+ pass
107
+
108
+ def test_getpath(self):
109
+ pass
110
+
111
+ def test_getroot(self):
112
+ with open('sample.xml') as f:
113
+ et = etree.parse(f)
114
+ tree = etree.parse('sample.xml')
115
+ root = tree.getroot()
116
+ self.assertEqual('{http://example.tld}document', root.tag)
117
+
118
+ def test_iter(self):
119
+ pass
120
+
121
+ def test_iterfind(self):
122
+ pass
123
+
124
+ def test_parse(self):
125
+ pass
126
+
127
+ def test_relaxng(self):
128
+ pass
129
+
130
+ def test_write(self):
131
+ with open('sample.xml') as f:
132
+ tree = etree.parse(f)
133
+
134
+ import tempfile
135
+ import os
136
+ fd, name = tempfile.mkstemp()
137
+ try:
138
+ with os.fdopen(fd, 'w') as f:
139
+ tree.write(f)
140
+ with open(name) as f:
141
+ tree2 = etree.parse(f)
142
+ finally:
143
+ os.unlink(name)
144
+ self.assertEqual(tree.getroot().tag, tree2.getroot().tag)
145
+
146
+ def test_write_c14n(self):
147
+ pass
148
+
149
+ def test_xinclude(self):
150
+ pass
151
+
152
+ def test_xmlschema(self):
153
+ pass
154
+
155
+ def test_xpath(self):
156
+ with open('sample.xml') as f:
157
+ et = etree.parse(f)
158
+ tree = etree.parse('sample.xml')
159
+
160
+ nsmap = dict(x='http://example.tld')
161
+
162
+ # element
163
+ result = tree.xpath('//x:paragraph', namespaces=nsmap)
164
+ self.assertEqual(1, len(result))
165
+ self.assertEqual('{http://example.tld}paragraph',
166
+ result[0].tag)
167
+
168
+ # attribute
169
+ result = tree.xpath('@version', namespaces=nsmap)
170
+ self.assertEqual(['1.0'], result)
171
+
172
+ # string
173
+ result = tree.xpath('"foo"', namespaces=nsmap)
174
+ self.assertEqual('foo', result)
175
+
176
+ # number expression
177
+ result = tree.xpath('1', namespaces=nsmap)
178
+ self.assertEqual(1.0, result)
179
+
180
+ result = tree.xpath('"1.0"', namespaces=nsmap)
181
+ # should be string, but alas, it returns a number in jxml
182
+ if sys.platform.startswith('java'):
183
+ self.assertEqual(1.0, result)
184
+ else:
185
+ self.assertEqual('1.0', result)
186
+
187
+ def test_xslt(self):
188
+ tree = etree.XML('<hello />').getroottree()
189
+ with open('xsl/import-test.xsl') as f:
190
+ xsl_tree = etree.parse(f)
191
+ result = tree.xslt(xsl_tree)
192
+ self.assertEqual('world', result.getroot().tag)
193
+
194
+
195
+ class ElementTest(TestCase):
196
+
197
+ def setUp(self):
198
+ with open('sample.xml') as f:
199
+ self.et = ElementTree(file=f)
200
+ self.root = self.et.getroot()
201
+
202
+ def tearDown(self):
203
+ pass
204
+
205
+ def test_Element(self):
206
+ elem = Element('document', dict(a='1', b='2'), c='3')
207
+ self.assertEqual('document', elem.tag)
208
+ self.assertEqual(dict(a='1', b='2', c='3'),
209
+ elem.attrib)
210
+
211
+ nsmap = dict(a='http://a.example.tld', c='http://c.example.tld')
212
+ elem = Element('{http://a.example.tld}document',
213
+ {'{http://a.example.tld}a': '1',
214
+ '{http://b.example.tld}a': '2',
215
+ 'a': '3'},
216
+ nsmap)
217
+
218
+ self.assertEqual('a', elem.prefix)
219
+ self.assertEqual('{http://a.example.tld}document', elem.tag)
220
+ self.assertEqual(nsmap['a'], elem.nsmap['a'])
221
+ self.assertEqual(nsmap['c'], elem.nsmap['c'])
222
+ self.assertTrue('http://b.example.tld' in elem.nsmap.values())
223
+ self.assertEqual('1', elem.get('{http://a.example.tld}a'))
224
+ self.assertEqual('2', elem.get('{http://b.example.tld}a'))
225
+ self.assertEqual('3', elem.get('a'))
226
+
227
+ def test_SubElement(self):
228
+ elem = Element('document')
229
+ child = SubElement(elem, 'paragraph')
230
+ grandchild = SubElement(child, 'span')
231
+ self.assertEqual('paragraph', elem[0].tag)
232
+ self.assertEqual('span', elem[0][0].tag)
233
+
234
+ def test_base(self):
235
+ uri = os.path.abspath('sample.xml')
236
+ with open(uri) as f:
237
+ et = etree.parse(f)
238
+ root = et.getroot()
239
+ self.assertEqual(uri, root.base)
240
+
241
+ def test_tag(self):
242
+ elem = Element('HwpDoc')
243
+ self.assertEqual('HwpDoc', elem.tag)
244
+
245
+ elem = Element('{http://www.w3.org/1999/XSL/Transform}stylesheet')
246
+ self.assertTrue(elem.prefix)
247
+ self.assertEqual('{http://www.w3.org/1999/XSL/Transform}stylesheet', elem.tag)
248
+
249
+ elem = Element('{http://www.w3.org/1999/XSL/Transform}stylesheet',
250
+ nsmap=dict(xsl='http://www.w3.org/1999/XSL/Transform'))
251
+ self.assertEqual('xsl', elem.prefix)
252
+ self.assertEqual('{http://www.w3.org/1999/XSL/Transform}stylesheet', elem.tag)
253
+
254
+ def test_nsmap(self):
255
+ self.assertEqual(dict(x='http://example.tld'),
256
+ self.root.nsmap)
257
+ self.assertEqual(dict(x='http://example.tld',
258
+ z='http://z.example.tld'),
259
+ self.root[1].nsmap)
260
+
261
+ def test_prefix(self):
262
+ self.assertEqual('x', self.root.prefix)
263
+ self.assertEqual('z', self.root[1].prefix)
264
+
265
+ def test_text(self):
266
+ self.assertEqual('text', self.root[0].text)
267
+ self.assertEqual(None, self.root[1].text)
268
+
269
+ def test_tail(self):
270
+ self.assertEqual('tail', self.root[0].tail)
271
+ self.assertEqual(None, self.root[0][0].tail)
272
+
273
+ def test_attrib(self):
274
+ self.assertEqual({'version': '1.0'},
275
+ self.root.attrib)
276
+ self.assertEqual({'a': '1',
277
+ '{http://example.tld}b': '2',
278
+ '{http://z.example.tld}c': '3'},
279
+ self.root[1].attrib)
280
+
281
+ def test__contains__(self):
282
+ pass
283
+
284
+ def test__copy__(self):
285
+ pass
286
+
287
+ def test__deepcopy__(self):
288
+ pass
289
+
290
+ def test__delitem__(self):
291
+ pass
292
+
293
+ def test__getitem__(self):
294
+ paragraph = self.root.__getitem__(0)
295
+ self.assertEqual('{http://example.tld}paragraph', paragraph.tag)
296
+ paragraph = self.root[0]
297
+ self.assertEqual('{http://example.tld}paragraph', paragraph.tag)
298
+
299
+ paragraph = self.root.__getitem__(1)
300
+ self.assertEqual('{http://z.example.tld}object', paragraph.tag)
301
+ paragraph = self.root[1]
302
+ self.assertEqual('{http://z.example.tld}object', paragraph.tag)
303
+
304
+ child = self.root.__getitem__(-1)
305
+ self.assertEqual('{http://example.tld}third', child.tag)
306
+ child = self.root[-1]
307
+ self.assertEqual('{http://example.tld}third', child.tag)
308
+
309
+ def test__iter__(self):
310
+ it = self.root.__iter__()
311
+
312
+ paragraph = it.next()
313
+ self.assertEqual('{http://example.tld}paragraph',
314
+ paragraph.tag)
315
+ self.assertEqual('text', paragraph.text)
316
+ self.assertEqual('tail', paragraph.tail)
317
+
318
+ paragraph = it.next()
319
+ self.assertEqual('{http://z.example.tld}object',
320
+ paragraph.tag)
321
+
322
+ child = it.next()
323
+ self.assertEqual('{http://example.tld}third',
324
+ child.tag)
325
+
326
+ self.assertRaises(StopIteration, it.next)
327
+
328
+ def test__len__(self):
329
+ self.assertEqual(3, self.root.__len__())
330
+ self.assertEqual(3, len(self.root))
331
+
332
+ def test__nonzero__(self):
333
+ pass
334
+
335
+ def test__repr__(self):
336
+ pass
337
+
338
+ def test__reversed__(self):
339
+ it = self.root.__reversed__()
340
+
341
+ child = it.next()
342
+ self.assertEqual('{http://example.tld}third',
343
+ child.tag)
344
+
345
+ paragraph = it.next()
346
+ self.assertEqual('{http://z.example.tld}object',
347
+ paragraph.tag)
348
+
349
+ paragraph = it.next()
350
+ self.assertEqual('{http://example.tld}paragraph',
351
+ paragraph.tag)
352
+ self.assertEqual('text', paragraph.text)
353
+ self.assertEqual('tail', paragraph.tail)
354
+
355
+ self.assertRaises(StopIteration, it.next)
356
+
357
+ def test__setitem__(self):
358
+ new_child = Element('new-child')
359
+ self.root.__setitem__(1, new_child)
360
+ self.assertEqual('new-child', self.root[1].tag)
361
+
362
+ new_child = Element('new-child2')
363
+ self.assertRaises(IndexError, self.root.__setitem__, 3, new_child)
364
+
365
+ def test_addnext(self):
366
+ pass
367
+
368
+ def test_addprevious(self):
369
+ pass
370
+
371
+ def test_append(self):
372
+ new_child = Element('new-child')
373
+ self.root.append(new_child)
374
+
375
+ child = self.root[3]
376
+ self.assertEqual('new-child', child.tag)
377
+
378
+ def test_clear(self):
379
+ pass
380
+
381
+ def test_extend(self):
382
+ pass
383
+
384
+ def test_find(self):
385
+ pass
386
+
387
+ def test_findall(self):
388
+ pass
389
+
390
+ def test_findtext(self):
391
+ pass
392
+
393
+ def test_get(self):
394
+ self.assertEqual(None, self.root.get('nonexists'))
395
+ self.assertEqual('1.0', self.root.get('version'))
396
+ self.assertEqual('1', self.root[1].get('a'))
397
+ self.assertEqual('2', self.root[1].get('{http://example.tld}b'))
398
+ self.assertEqual('3', self.root[1].get('{http://z.example.tld}c'))
399
+
400
+ def test_getchildren(self):
401
+ children = self.root.getchildren()
402
+
403
+ child = children[0]
404
+ self.assertEqual('{http://example.tld}paragraph',
405
+ child.tag)
406
+ self.assertEqual('text', child.text)
407
+ self.assertEqual('tail', child.tail)
408
+
409
+ child = children[1]
410
+ self.assertEqual('{http://z.example.tld}object',
411
+ child.tag)
412
+
413
+ child = children[2]
414
+ self.assertEqual('{http://example.tld}third',
415
+ child.tag)
416
+
417
+ def test_getiterator(self):
418
+ pass
419
+
420
+ def test_getnext(self):
421
+ child = self.root[0]
422
+ child = child.getnext()
423
+ self.assertEqual('{http://z.example.tld}object',
424
+ child.tag)
425
+ child = child.getnext()
426
+ self.assertEqual('{http://example.tld}third',
427
+ child.tag)
428
+ child = child.getnext()
429
+ self.assertEqual(None, child)
430
+
431
+ def test_getparent(self):
432
+ parent = self.root.getparent()
433
+ self.assertEqual(None, parent)
434
+
435
+ for child in self.root:
436
+ parent = child.getparent()
437
+ self.assertEqual('{http://example.tld}document',
438
+ parent.tag)
439
+
440
+ def test_getprevious(self):
441
+ child = self.root[-1]
442
+ self.assertEqual('{http://example.tld}third',
443
+ child.tag)
444
+ child = child.getprevious()
445
+ self.assertEqual('{http://z.example.tld}object',
446
+ child.tag)
447
+ child = child.getprevious()
448
+ self.assertEqual('{http://example.tld}paragraph',
449
+ child.tag)
450
+ child = child.getprevious()
451
+ self.assertEqual(None, child)
452
+
453
+ def test_getroottree(self):
454
+ elem = Element('HwpDoc')
455
+ self.assertTrue(elem.getroottree() is not None)
456
+
457
+ def test_index(self):
458
+ pass
459
+
460
+ def test_insert(self):
461
+ pass
462
+
463
+ def test_items(self):
464
+ pass
465
+
466
+ def test_iter(self):
467
+ pass
468
+
469
+ def test_iterancestors(self):
470
+ span = self.root[0][0]
471
+ it = span.iterancestors()
472
+
473
+ parent = it.next()
474
+ self.assertEqual('{http://example.tld}paragraph',
475
+ parent.tag)
476
+
477
+ parent = it.next()
478
+ self.assertEqual('{http://example.tld}document',
479
+ parent.tag)
480
+
481
+ self.assertRaises(StopIteration, it.next)
482
+
483
+ # with tags predicate
484
+
485
+ it = span.iterancestors('{http://example.tld}document')
486
+
487
+ parent = it.next()
488
+ self.assertEqual('{http://example.tld}document',
489
+ parent.tag)
490
+
491
+ self.assertRaises(StopIteration, it.next)
492
+
493
+ def test_iterchildren(self):
494
+ pass
495
+
496
+ def test_descendants(self):
497
+ pass
498
+
499
+ def test_iterfind(self):
500
+ pass
501
+
502
+ def test_siblings(self):
503
+ pass
504
+
505
+ def test_itertext(self):
506
+ pass
507
+
508
+ def test_keys(self):
509
+ self.assertEqual(set(['version']),
510
+ set(self.root.keys()))
511
+ self.assertEqual(set(['a', '{http://example.tld}b',
512
+ '{http://z.example.tld}c']),
513
+ set(self.root[1].keys()))
514
+
515
+ def test_makeelement(self):
516
+ pass
517
+
518
+ def test_remove(self):
519
+ pass
520
+
521
+ def test_replace(self):
522
+ pass
523
+
524
+ def test_set(self):
525
+ self.root.set('{http://example.tld}a', '1')
526
+ self.assertEqual('1', self.root.get('{http://example.tld}a'))
527
+ self.root.set('{http://c.example.tld}a', '2')
528
+ self.assertEqual('2', self.root.get('{http://c.example.tld}a'))
529
+ self.root.set('a', '3')
530
+ self.assertEqual('3', self.root.get('a'))
531
+
532
+ def test_values(self):
533
+ self.root[1].values()
534
+
535
+ def test_xpath(self):
536
+ nsmap = dict(x='http://example.tld')
537
+
538
+ # element
539
+ result = self.root.xpath('//x:paragraph', namespaces=nsmap)
540
+ self.assertEqual(1, len(result))
541
+ self.assertEqual('{http://example.tld}paragraph',
542
+ result[0].tag)
543
+
544
+ # attribute
545
+ result = self.root.xpath('@version', namespaces=nsmap)
546
+ self.assertEqual(['1.0'], result)
547
+
548
+ # string
549
+ result = self.root.xpath('"foo"', namespaces=nsmap)
550
+ self.assertEqual('foo', result)
551
+
552
+ # number expression
553
+ result = self.root.xpath('1', namespaces=nsmap)
554
+ self.assertEqual(1.0, result)
555
+
556
+ result = self.root.xpath('"1.0"', namespaces=nsmap)
557
+ # should be string, but alas, it returns a number in jxml
558
+ if sys.platform.startswith('java'):
559
+ self.assertEqual(1.0, result)
560
+ else:
561
+ self.assertEqual('1.0', result)
562
+
563
+
564
+ class QNameTest(TestCase):
565
+
566
+ text = '{http://example.tld}document'
567
+ namespace = 'http://example.tld'
568
+ localname = 'document'
569
+
570
+ def test_from_text(self):
571
+ qname = QName(self.text)
572
+ self.assertEqual(self.text, qname.text)
573
+ self.assertEqual(self.namespace, qname.namespace)
574
+ self.assertEqual(self.localname, qname.localname)
575
+
576
+ qname = QName('document')
577
+ self.assertEqual('document', qname.text)
578
+ self.assertEqual(None, qname.namespace)
579
+ self.assertEqual('document', qname.localname)
580
+
581
+ def test_from_nsuri_and_tag(self):
582
+ qname = QName(self.namespace, self.localname)
583
+ self.assertEqual(self.text, qname.text)
584
+ self.assertEqual(self.namespace, qname.namespace)
585
+ self.assertEqual(self.localname, qname.localname)
586
+
587
+ def test_from_element(self):
588
+ element = Element(self.text)
589
+ qname = QName(element)
590
+ self.assertEqual(self.text, qname.text)
591
+ self.assertEqual(self.namespace, qname.namespace)
592
+ self.assertEqual(self.localname, qname.localname)
593
+
594
+
595
+ class XPathTest(TestCase):
596
+
597
+ def test_xpath(self):
598
+ from lxml.etree import parse
599
+ from lxml.etree import XPath
600
+ with file('sample.xml') as f:
601
+ doc = parse(f)
602
+
603
+ nsmap = dict(x='http://example.tld')
604
+
605
+ # element
606
+ xpath = XPath('//x:paragraph', namespaces=nsmap)
607
+ result = xpath(doc)
608
+ self.assertEqual(1, len(result))
609
+ self.assertEqual('{http://example.tld}paragraph',
610
+ result[0].tag)
611
+
612
+ # attribute
613
+ xpath = XPath('@version', namespaces=nsmap)
614
+ result = xpath(doc)
615
+ self.assertEqual(['1.0'], result)
616
+
617
+ # string
618
+ xpath = XPath('"foo"', namespaces=nsmap)
619
+ result = xpath(doc)
620
+ self.assertEqual('foo', result)
621
+
622
+ # number
623
+ xpath = XPath('1', namespaces=nsmap)
624
+ self.assertEqual(1, xpath(doc))
625
+
626
+ # string, but alas, it returns a number in jxml
627
+ xpath = XPath('"1.0"', namespaces=nsmap)
628
+ result = xpath(doc)
629
+ if sys.platform.startswith('java'):
630
+ self.assertEqual(1.0, result)
631
+ else:
632
+ self.assertEqual('1.0', result)
633
+
634
+ # Boolean
635
+ xpath = XPath('1 = 1', namespaces=nsmap)
636
+ self.assertEqual(True, xpath(doc))
637
+
638
+
639
+ class XSLTTest(TestCase):
640
+
641
+ def test_from_element(self):
642
+ with open('xsl/import-test.xsl') as f:
643
+ xsl_tree = etree.parse(f)
644
+ etree.XSLT(xsl_tree.getroot())
645
+
646
+ def test_xslt_with_default_parser(self):
647
+ with open('xsl/import-test.xsl') as f:
648
+ xsl_tree = etree.parse(f)
649
+ transform = etree.XSLT(xsl_tree)
650
+ result = transform(etree.XML('<hello />'))
651
+ self.assertEqual('world', result.getroot().tag)
652
+
653
+ def test_text_output(self):
654
+ with open('text-output.xsl') as f:
655
+ xsl_tree = etree.parse(f)
656
+ transform = etree.XSLT(xsl_tree)
657
+ result = transform(etree.XML('<hello/>'))
658
+ self.assertEqual(None, result.getroot())
659
+ self.assertEqual(u'world', unicode(result))
660
+ self.assertEqual('world', str(result))
661
+
662
+
663
+ if __name__ == '__main__':
664
+ unittest.main()
tools/jxml/tests/text-output.xsl ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet version="1.0"
3
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4
+ >
5
+ <xsl:output method="text" encoding="utf-8" indent="no" />
6
+ <xsl:template match="/hello">world</xsl:template>
7
+ </xsl:stylesheet>
tools/jxml/tests/xsl/import-test.xsl ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet version="1.0"
3
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4
+ >
5
+ <xsl:import href="imported.xsl" />
6
+ <xsl:output method="xml" encoding="utf-8" indent="no" />
7
+ <xsl:template match="/">
8
+ <xsl:apply-templates />
9
+ </xsl:template>
10
+ </xsl:stylesheet>
tools/jxml/tests/xsl/imported.xsl ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xsl:stylesheet version="1.0"
3
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4
+ >
5
+ <xsl:output method="xml" encoding="utf-8" indent="no" />
6
+ <xsl:template match="hello"><world /></xsl:template>
7
+ </xsl:stylesheet>
tools/oxt.tool/oxt_tool/__init__.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import logging
3
+
4
+
5
+ def console(soffice='soffice'):
6
+ import uno
7
+ import unokit.contexts
8
+ import unokit.services
9
+ import oxt_tool.remote
10
+
11
+ with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
12
+ desktop = unokit.services.css.frame.Desktop()
13
+ def new_textdoc():
14
+ return desktop.loadComponentFromURL('private:factory/swriter',
15
+ '_blank', 0, tuple())
16
+ from unokit.util import dump, dumpdir
17
+ local = dict(uno=uno, context=context,
18
+ css=unokit.services.css, dump=dump, dumpdir=dumpdir,
19
+ desktop=desktop, new_textdoc=new_textdoc)
20
+ __import__('code').interact(banner='oxt-console', local=local)
21
+
22
+
23
+ def dict_to_namedvalue(d):
24
+ import uno
25
+ nv = list()
26
+ for n, v in d.items():
27
+ if isinstance(v, dict):
28
+ v = dict_to_namedvalue(v)
29
+ item = uno.createUnoStruct('com.sun.star.beans.NamedValue')
30
+ item.Name = n
31
+ item.Value = v
32
+ nv.append(item)
33
+ return tuple(nv)
34
+
35
+
36
+ def test_remotely(soffice, discover_dirs, extra_path, logconf_path):
37
+ import sys
38
+ import os
39
+ import os.path
40
+ import discover
41
+ import oxt_tool.remote
42
+
43
+ logger = logging.getLogger('unokit')
44
+ logger.addHandler(logging.StreamHandler())
45
+ logger.setLevel(logging.INFO)
46
+
47
+ logfmt = logging.Formatter(('frontend %5d ' % os.getpid())
48
+ +'%(message)s')
49
+ logchn = logging.StreamHandler()
50
+ logchn.setFormatter(logfmt)
51
+ logger = logging.getLogger('frontend')
52
+ logger.addHandler(logchn)
53
+ logger.setLevel(logging.INFO)
54
+
55
+ working_dir = sys.argv[1]
56
+ working_dir = os.path.abspath(working_dir)
57
+
58
+ for path in sys.path:
59
+ logger.info('sys.path: %s', path)
60
+
61
+ if logconf_path:
62
+ logconf_path = os.path.abspath(logconf_path)
63
+
64
+ backend_path = sys.modules['oxt_tool'].__file__
65
+ backend_path = os.path.dirname(backend_path)
66
+ backend_path = os.path.join(backend_path, 'backend.py')
67
+ backend_name = 'backend.TestRunnerJob'
68
+
69
+ tss = []
70
+ for d in discover_dirs:
71
+ d = os.path.abspath(d)
72
+ logger.info('discover tests: %s', d)
73
+ testloader = discover.DiscoveringTestLoader()
74
+ testsuite = testloader.discover(d)
75
+ tss.append(testsuite)
76
+ import unittest
77
+ testsuite = unittest.TestSuite(tss)
78
+
79
+ with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
80
+ logger.info('remote context created')
81
+ factory = load_component(backend_path, backend_name)
82
+ if factory:
83
+ backendjob = factory.createInstanceWithContext(context)
84
+ if backendjob:
85
+ import cPickle
86
+ from unokit.adapters import OutputStreamToFileLike
87
+ pickled_testsuite = cPickle.dumps(testsuite)
88
+ outputstream = OutputStreamToFileLike(sys.stderr)
89
+ logstream = OutputStreamToFileLike(sys.stderr)
90
+ args = dict(outputstream=outputstream,
91
+ logstream=logstream,
92
+ pickled_testsuite=pickled_testsuite,
93
+ extra_path=tuple(extra_path),
94
+ logconf_path=logconf_path,
95
+ working_dir=working_dir)
96
+ args = dict_to_namedvalue(args)
97
+ result = backendjob.execute(args)
98
+ result = str(result)
99
+ result = cPickle.loads(result)
100
+ return 0 if result['successful'] else 1
101
+ return -1
102
+
103
+
104
+ def load_component(component_path, component_name):
105
+ import os
106
+ import os.path
107
+ import uno
108
+ from unokit.services import css
109
+ loader = css.loader.Python()
110
+ if loader:
111
+ component_path = os.path.abspath(component_path)
112
+ component_url = uno.systemPathToFileUrl(component_path)
113
+
114
+ return loader.activate(component_name, '',
115
+ component_url, None)
116
+
117
+
118
+ def console_in_proc(soffice='soffice'):
119
+ import os
120
+ import unohelper
121
+ from com.sun.star.task import XJob
122
+ import oxt_tool.remote
123
+
124
+ logfmt = logging.Formatter(('frontend %5d ' % os.getpid())
125
+ +'%(message)s')
126
+ logchn = logging.StreamHandler()
127
+ logchn.setFormatter(logfmt)
128
+ logger = logging.getLogger('frontend')
129
+ logger.addHandler(logchn)
130
+ logger.setLevel(logging.INFO)
131
+
132
+ class ConsoleInput(unohelper.Base, XJob):
133
+
134
+ def __init__(self, context):
135
+ self.context = context
136
+
137
+ def execute(self, arguments):
138
+ prompt, = arguments
139
+ try:
140
+ return raw_input(prompt.Value)
141
+ except EOFError:
142
+ return None
143
+
144
+ import sys
145
+ import os.path
146
+ backend_path = sys.modules['oxt_tool'].__file__
147
+ backend_path = os.path.dirname(backend_path)
148
+ backend_path = os.path.join(backend_path, 'backend.py')
149
+ backend_name = 'backend.ConsoleJob'
150
+
151
+ with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
152
+ logger.info('remote context created')
153
+ factory = load_component(backend_path, backend_name)
154
+ if factory:
155
+ backendjob = factory.createInstanceWithContext(context)
156
+ if backendjob:
157
+ from unokit.adapters import OutputStreamToFileLike
158
+ outstream = OutputStreamToFileLike(sys.stderr)
159
+ args = dict(inp=ConsoleInput(context),
160
+ outstream=outstream)
161
+ args = dict_to_namedvalue(args)
162
+ backendjob.execute(args)
163
+
164
+
165
+ class LoEnviron(object):
166
+
167
+ def __init__(self, program_dir):
168
+ self.program_dir = program_dir
169
+
170
+ @property
171
+ def rc_ext(self):
172
+ import sys
173
+ if sys.platform == 'win32':
174
+ return '.ini'
175
+ else:
176
+ return 'rc'
177
+
178
+
179
+ @property
180
+ def fundamentalrc(self):
181
+ import os.path
182
+ filename = 'fundamental' + self.rc_ext
183
+ return os.path.join(self.program_dir, filename)
184
+
185
+
186
+ @property
187
+ def ure_bootstrap(self):
188
+ return 'vnd.sun.star.pathname:' + self.fundamentalrc
189
+
190
+
191
+ def run_in_lo(soffice='soffice'):
192
+ import os
193
+ import sys
194
+ import os.path
195
+
196
+ uno_pythonpath = os.environ['UNO_PYTHONPATH'].split(os.pathsep)
197
+ sys.path.extend(uno_pythonpath)
198
+
199
+ loenv = LoEnviron(os.environ['LO_PROGRAM'])
200
+ os.environ['URE_BOOTSTRAP'] = loenv.ure_bootstrap
201
+
202
+ import oxt_tool.remote
203
+
204
+ logger = logging.getLogger('unokit')
205
+ logger.addHandler(logging.StreamHandler())
206
+ logger.setLevel(logging.INFO)
207
+
208
+ logger = logging.getLogger('oxt_tool')
209
+ logger.addHandler(logging.StreamHandler())
210
+ logger.setLevel(logging.INFO)
211
+
212
+ logfmt = logging.Formatter(('frontend %5d ' % os.getpid())
213
+ +'%(message)s')
214
+ logchn = logging.StreamHandler()
215
+ logchn.setFormatter(logfmt)
216
+ logger = logging.getLogger('frontend')
217
+ logger.addHandler(logchn)
218
+ logger.setLevel(logging.INFO)
219
+
220
+ for path in sys.path:
221
+ logger.info('sys.path: %s', path)
222
+
223
+ working_dir = os.getcwd()
224
+ working_dir = os.path.abspath(working_dir)
225
+ argv = list(sys.argv[1:])
226
+
227
+ if argv[0].startswith('--logfile='):
228
+ logfile = argv[0][len('--logfile='):]
229
+ argv = argv[1:]
230
+ else:
231
+ logfile = None
232
+ argv[0] = os.path.abspath(argv[0])
233
+ print argv
234
+
235
+ backend_path = sys.modules['oxt_tool'].__file__
236
+ backend_path = os.path.dirname(backend_path)
237
+ backend_path = os.path.join(backend_path, 'backend.py')
238
+ backend_name = 'backend.RemoteRunJob'
239
+
240
+ with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
241
+ logger.info('remote context created')
242
+ factory = load_component(backend_path, backend_name)
243
+ if factory:
244
+ backendjob = factory.createInstanceWithContext(context)
245
+ if backendjob:
246
+ import cPickle
247
+ from unokit.adapters import OutputStreamToFileLike
248
+ from unokit.adapters import InputStreamFromFileLike
249
+ stdin = InputStreamFromFileLike(sys.stdin)
250
+ stdout = OutputStreamToFileLike(sys.stdout)
251
+ stderr = OutputStreamToFileLike(sys.stderr)
252
+ path = cPickle.dumps(sys.path)
253
+ argv = cPickle.dumps(argv)
254
+ args = dict(logfile=logfile,
255
+ working_dir=working_dir,
256
+ path=path,
257
+ argv=argv,
258
+ stdin=stdin,
259
+ stdout=stdout,
260
+ stderr=stderr)
261
+ args = dict_to_namedvalue(args)
262
+ return backendjob.execute(args)
263
+ return -1
264
+
265
+
266
+ def install(unopkg='unopkg'):
267
+ import sys
268
+ cmd = [unopkg, 'add', '-v', '-s']
269
+ cmd.extend(sys.argv[1:])
270
+ cmd = [('"%s"' % x) if ' ' not in x else x
271
+ for x in cmd]
272
+ cmd = ' '.join(cmd)
273
+ import os
274
+ return os.system(cmd)
275
+
276
+
277
+ class Console(object):
278
+ def __init__(self, buildout, name, options):
279
+ import os.path
280
+ self.__name = name
281
+ self.__logger = logging.getLogger(name)
282
+ self.__bindir = buildout['buildout']['bin-directory']
283
+
284
+ soffice = options.get('soffice', 'soffice').strip()
285
+ if not os.path.exists(soffice):
286
+ self.__skip = True
287
+ self.__logger.info('soffice not found at: %s', soffice)
288
+ self.__logger.info('installation will be skipped')
289
+ return
290
+
291
+ in_proc = options.get('in_proc', 'true').strip().lower()
292
+ in_proc = in_proc in ['true', 'yes', '1']
293
+
294
+ self.__python = options.get('python', '').strip()
295
+ self.__soffice = soffice
296
+ self.__in_proc = in_proc
297
+ self.__skip = False
298
+
299
+ def install(self):
300
+ if self.__skip:
301
+ self.__logger.info('skipped')
302
+ return []
303
+
304
+ from zc.buildout import easy_install
305
+ import pkg_resources
306
+ import sys
307
+ ws = [pkg_resources.get_distribution(dist)
308
+ for dist in ['unokit', 'oxt.tool']]
309
+ if self.__in_proc:
310
+ func = 'console_in_proc'
311
+ else:
312
+ func = 'console'
313
+ entrypoints = [(self.__name, 'oxt_tool', func)]
314
+ arguments = '%r' % self.__soffice
315
+ if self.__python:
316
+ python = self.__python
317
+ else:
318
+ python = sys.executable
319
+ return easy_install.scripts(entrypoints,
320
+ ws, python, self.__bindir,
321
+ arguments=arguments)
322
+
323
+ update = install
324
+
325
+
326
+ class TestRunner(object):
327
+ def __init__(self, buildout, name, options):
328
+ import os.path
329
+ self.__name = name
330
+ self.__logger = logging.getLogger(name)
331
+ self.__bindir = buildout['buildout']['bin-directory']
332
+ self.__skip = False
333
+ self.__python = options.get('python', '').strip()
334
+
335
+ self.__soffice = options.get('soffice', 'soffice').strip()
336
+ if not os.path.exists(self.__soffice):
337
+ self.__skip = True
338
+ self.__logger.info('soffice not found at: %s', self.__soffice)
339
+ self.__logger.info('installation will be skipped')
340
+ return
341
+ self.__discover = options['discover'].split()
342
+ self.__extra_path = options['extra_path'].split()
343
+ self.__logconf_path = options.get('logconf_path')
344
+
345
+ def install(self):
346
+ if self.__skip:
347
+ self.__logger.info('skipped')
348
+ return []
349
+
350
+ from zc.buildout import easy_install
351
+ import pkg_resources
352
+ import sys
353
+ ws = [pkg_resources.get_distribution(dist)
354
+ for dist in ['unokit', 'oxt.tool', 'discover']]
355
+ entrypoints = [(self.__name, 'oxt_tool', 'test_remotely')]
356
+ arguments = '%r, %r, %r, %r'
357
+ arguments = arguments % (self.__soffice,
358
+ self.__discover,
359
+ self.__extra_path,
360
+ self.__logconf_path)
361
+ if self.__python:
362
+ python = self.__python
363
+ else:
364
+ python = sys.executable
365
+ return easy_install.scripts(entrypoints,
366
+ ws, python, self.__bindir,
367
+ arguments=arguments)
368
+
369
+ update = install
370
+
371
+
372
+ class Installer(object):
373
+ def __init__(self, buildout, name, options):
374
+ import os.path
375
+ self.__name = name
376
+ self.__logger = logging.getLogger(name)
377
+ self.__bindir = buildout['buildout']['bin-directory']
378
+
379
+ unopkg = options.get('unopkg', 'unopkg').strip()
380
+ if not os.path.exists(unopkg):
381
+ self.__skip = True
382
+ self.__logger.info('unopkg not found at: %s', unopkg)
383
+ self.__logger.info('installation will be skipped')
384
+ return
385
+
386
+ self.__unopkg = unopkg
387
+ self.__skip = False
388
+
389
+ def install(self):
390
+ if self.__skip:
391
+ self.__logger.info('skipped')
392
+ return []
393
+
394
+ from zc.buildout import easy_install
395
+ import pkg_resources
396
+ import sys
397
+ ws = [pkg_resources.get_distribution(dist)
398
+ for dist in ['unokit', 'oxt.tool']]
399
+ entrypoints = [(self.__name, 'oxt_tool', 'install')]
400
+ arguments = '%r' % self.__unopkg
401
+ return easy_install.scripts(entrypoints,
402
+ ws, sys.executable, self.__bindir,
403
+ arguments=arguments)
404
+
405
+ update = install
tools/oxt.tool/oxt_tool/backend.py ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ import logging
3
+ import sys
4
+ import os
5
+
6
+ logfmt = logging.Formatter((' backend %5d ' % os.getpid())
7
+ +'%(message)s')
8
+ logger = logging.getLogger('backend')
9
+ logger.setLevel(logging.INFO)
10
+
11
+
12
+ logger.info('backend: %s', os.getpid())
13
+ logger.info('sys.executable = %s', sys.executable)
14
+
15
+ import unohelper
16
+ g_ImplementationHelper = unohelper.ImplementationHelper()
17
+
18
+ def implementation(component_name, *services):
19
+ def decorator(cls):
20
+ g_ImplementationHelper.addImplementation(cls, component_name, services)
21
+ return cls
22
+ return decorator
23
+
24
+
25
+ from com.sun.star.task import XJob
26
+
27
+ @implementation('backend.TestRunnerJob')
28
+ class TestRunnerJob(unohelper.Base, XJob):
29
+
30
+ def __init__(self, context):
31
+ self.context = context
32
+
33
+ def execute(self, arguments):
34
+ import sys
35
+ args = dict((nv.Name, nv.Value) for nv in arguments)
36
+
37
+ cwd = os.getcwd()
38
+ working_dir = args['working_dir']
39
+ os.chdir(working_dir)
40
+ try:
41
+ logstream = args['logstream']
42
+ logstream = FileFromStream(logstream)
43
+ loghandler = logging.StreamHandler(logstream)
44
+ loghandler.setFormatter(logfmt)
45
+ logger.addHandler(loghandler)
46
+ try:
47
+ logger.info('current dir: %s', cwd)
48
+ logger.info('working dir: %s', working_dir)
49
+ logger.info('sys.path:')
50
+ for x in sys.path:
51
+ logger.info('- %s', x)
52
+ return self.run(args)
53
+ finally:
54
+ logger.removeHandler(loghandler)
55
+ finally:
56
+ os.chdir(cwd)
57
+
58
+ def run(self, args):
59
+ import cPickle
60
+ outstream = args.get('outputstream')
61
+ outstream = FileFromStream(outstream)
62
+
63
+ extra_path = args.get('extra_path')
64
+ if extra_path:
65
+ logger.info('extra_path: %s', ' '.join(extra_path))
66
+ sys.path.extend(extra_path)
67
+
68
+ logconf_path = args.get('logconf_path')
69
+ if logconf_path:
70
+ import logging.config
71
+ logging.config.fileConfig(logconf_path)
72
+
73
+ from hwp5.plat import _uno
74
+ _uno.enable()
75
+
76
+ pickled_testsuite = args.get('pickled_testsuite')
77
+ if not pickled_testsuite:
78
+ logger.error('pickled_testsuite is required')
79
+ return cPickle.dumps(dict(successful=False, tests=0, failures=0,
80
+ errors=0))
81
+
82
+ pickled_testsuite = str(pickled_testsuite)
83
+ testsuite = cPickle.loads(pickled_testsuite)
84
+ logger.info('Test Suite Unpickled')
85
+
86
+ from unittest import TextTestRunner
87
+ testrunner = TextTestRunner(stream=outstream)
88
+ result = testrunner.run(testsuite)
89
+ result = dict(successful=result.wasSuccessful(),
90
+ tests=result.testsRun,
91
+ failures=list(str(x) for x in result.failures),
92
+ errors=list(str(x) for x in result.errors))
93
+ return cPickle.dumps(result)
94
+
95
+
96
+ import contextlib
97
+
98
+
99
+ @contextlib.contextmanager
100
+ def sandbox(working_dir, **kwargs):
101
+ import os
102
+ import sys
103
+
104
+ backup = dict()
105
+ class NOTHING:
106
+ pass
107
+ if not hasattr(sys, 'argv'):
108
+ sys.argv = NOTHING
109
+
110
+ NAMES = ['path', 'argv', 'stdin', 'stdout', 'stderr']
111
+ for x in NAMES:
112
+ assert x in kwargs
113
+
114
+ backup['cwd'] = os.getcwd()
115
+ os.chdir(working_dir)
116
+ for x in NAMES:
117
+ backup[x] = getattr(sys, x)
118
+ setattr(sys, x, kwargs[x])
119
+
120
+ try:
121
+ yield
122
+ finally:
123
+ for x in NAMES:
124
+ setattr(sys, x, backup[x])
125
+ os.chdir(backup['cwd'])
126
+
127
+ if sys.argv is NOTHING:
128
+ del sys.argv
129
+
130
+
131
+ @implementation('backend.RemoteRunJob')
132
+ class RemoteRunJob(unohelper.Base, XJob):
133
+
134
+ def __init__(self, context):
135
+ self.context = context
136
+
137
+ def execute(self, arguments):
138
+ args = dict((nv.Name, nv.Value) for nv in arguments)
139
+
140
+ logpath = args.get('logfile')
141
+ if logpath is not None:
142
+ logfile = file(logpath, 'a')
143
+ loghandler = logging.StreamHandler(logfile)
144
+ logger.addHandler(loghandler)
145
+
146
+ import datetime
147
+ logger.info('-'*10 + (' start at %s' % datetime.datetime.now()) + '-'*10)
148
+ try:
149
+ return self.run(args)
150
+ finally:
151
+ logger.info('-'*10 + (' stop at %s' % datetime.datetime.now()) + '-'*10)
152
+ if logpath is not None:
153
+ logger.removeHandler(loghandler)
154
+
155
+ def run(self, args):
156
+ import cPickle
157
+
158
+ working_dir = args['working_dir']
159
+ path = cPickle.loads(str(args['path']))
160
+ argv = cPickle.loads(str(args['argv']))
161
+ stdin = FileFromStream(args['stdin'])
162
+ stdout = FileFromStream(args['stdout'])
163
+ stderr = FileFromStream(args['stderr'])
164
+
165
+ script = argv[0]
166
+ with sandbox(working_dir, path=path, argv=argv, stdin=stdin,
167
+ stdout=stdout, stderr=stderr):
168
+ g = dict(__name__='__main__')
169
+ try:
170
+ execfile(script, g)
171
+ except SystemExit, e:
172
+ return e.code
173
+ except Exception, e:
174
+ logger.exception(e)
175
+ raise
176
+ except:
177
+ import traceback
178
+ logger.error('%s' % traceback.format_exc())
179
+ raise
180
+
181
+
182
+ @implementation('backend.ConsoleJob')
183
+ class ConsoleJob(unohelper.Base, XJob):
184
+
185
+ def __init__(self, context):
186
+ self.context = context
187
+
188
+ def execute(self, arguments):
189
+ args = dict((nv.Name, nv.Value) for nv in arguments)
190
+
191
+ cwd = os.getcwd()
192
+ try:
193
+ inp = args['inp']
194
+ outstream = args['outstream']
195
+
196
+ outfile = FileFromStream(outstream)
197
+
198
+ import sys
199
+ orig = sys.stdout, sys.stderr
200
+ sys.stdout = sys.stderr = outfile
201
+ try:
202
+ console = Console(inp, outfile)
203
+ try:
204
+ console.interact('LibreOffice Python Console (pid: %s)' %
205
+ os.getpid())
206
+ return 0
207
+ except SystemExit, e:
208
+ return e.code
209
+ finally:
210
+ sys.stdout, sys.stderr = orig
211
+ finally:
212
+ os.chdir(cwd)
213
+
214
+
215
+ from code import InteractiveConsole
216
+ class Console(InteractiveConsole):
217
+
218
+ def __init__(self, inp, outfile):
219
+ InteractiveConsole.__init__(self)
220
+ self.inp = inp
221
+ self.outfile = outfile
222
+
223
+ def write(self, data):
224
+ self.outfile.write(data)
225
+ self.outfile.flush()
226
+
227
+ def raw_input(self, prompt=''):
228
+ import uno
229
+ arg = uno.createUnoStruct('com.sun.star.beans.NamedValue')
230
+ arg.Name = 'prompt'
231
+ arg.Value = prompt
232
+ args = arg,
233
+ result = self.inp.execute(args)
234
+ if result is None:
235
+ raise EOFError()
236
+ return result
237
+
238
+
239
+ class FileFromStream(object):
240
+ ''' A file-like object based on XInputStream/XOuputStream/XSeekable
241
+
242
+ :param stream: a stream object which implements
243
+ com.sun.star.io.XInputStream, com.sun.star.io.XOutputStream or
244
+ com.sun.star.io.XSeekable
245
+ '''
246
+ def __init__(self, stream, encoding='utf-8'):
247
+ import uno
248
+ self.stream = stream
249
+ self.encoding = encoding
250
+
251
+ if hasattr(stream, 'readBytes'):
252
+ def read(size=None):
253
+ if size is None:
254
+ data = ''
255
+ while True:
256
+ bytes = uno.ByteSequence('')
257
+ n_read, bytes = stream.readBytes(bytes, 4096)
258
+ if n_read == 0:
259
+ return data
260
+ data += bytes.value
261
+ bytes = uno.ByteSequence('')
262
+ n_read, bytes = stream.readBytes(bytes, size)
263
+ return bytes.value
264
+ self.read = read
265
+
266
+ if hasattr(stream, 'seek'):
267
+ self.tell = stream.getPosition
268
+
269
+ def seek(offset, whence=0):
270
+ if whence == 0:
271
+ pass
272
+ elif whence == 1:
273
+ offset += stream.getPosition()
274
+ elif whence == 2:
275
+ offset += stream.getLength()
276
+ stream.seek(offset)
277
+ self.seek = seek
278
+
279
+ if hasattr(stream, 'writeBytes'):
280
+ def write(s):
281
+ if isinstance(s, unicode):
282
+ s = s.encode(self.encoding)
283
+ stream.writeBytes(uno.ByteSequence(s))
284
+ self.write = write
285
+
286
+ def flush():
287
+ stream.flush()
288
+ self.flush = flush
289
+
290
+ def close(self):
291
+ if hasattr(self.stream, 'closeInput'):
292
+ self.stream.closeInput()
293
+ elif hasattr(self.stream, 'closeOutput'):
294
+ self.stream.closeOutput()
295
+
296
+
297
+ '''
298
+ import os.path
299
+ from uno import systemPathToFileUrl
300
+ from unokit.ucb import open_url
301
+ from unokit.services import css
302
+
303
+ path = os.path.abspath('samples/sample-5017.hwp')
304
+ print path
305
+ url = systemPathToFileUrl(path)
306
+ print url
307
+ inp = open_url(url)
308
+ print inp
309
+
310
+ # 여기서 segfault
311
+ stg = css.embed.OLESimpleStorage(inp)
312
+ print stg
313
+ '''
314
+
315
+
316
+ '''
317
+ SegFault가 나는 stacktrace는 다음과 같다
318
+
319
+ StgDirEntry::StgDirEntry
320
+ sot/source/sdstor/
321
+ StgEntry::Load
322
+ sot/source/sdstor/
323
+ ToUpperUnicode
324
+ sot/source/sdstor/stgelem.cxx
325
+ CharClass
326
+ unotools/source/i18n/charclass.cxx
327
+ intl_createInstance
328
+ unotools/source/i18n/instance.hxx
329
+
330
+ 여기서 ::comphelper::getProcessServiceFactory로 얻은 XMultiServiceFactory가
331
+ null 값인 것 같다.
332
+
333
+ ----
334
+
335
+ uno를 사용하는 프로그램들 (desktop app/unopkg, 각종 unittest 프로그램)은
336
+ 처음 실행 시 다음과 같은 과정을 거치는 듯 하다.
337
+
338
+ 1. ::cppu::defaultBootstrap_InitialComponentContext()을 호출, local context 생성
339
+ - unorc 등을 검색, application.rdb, user.rdb 등에 접근
340
+
341
+ - pyuno.so 의 getComponentContext()에서 수행: PYUNOLIBDIR 즉 pyuno.so가 있는
342
+ 디렉토리에서 pyuno.rc가 있으면 그것으로 초기화)
343
+ - desktop app: appinit.cxx의 CreateApplicationServiceManager()에서 수행
344
+ - unopkg: unopkg_misc.cxx의 bootstrapStandAlone()에서 수행.
345
+ ucbhelper::ContentBroker도 함께 초기화한다.
346
+
347
+ 2. 이렇게 생성한 local context로부터 ServiceManager를 얻어
348
+ ::comphelper::setProcessServiceFactory()를 사용하여 프로세스 전역 service
349
+ factory로 등록
350
+
351
+ - uno.py와 pyuno.so는 이 작업을 하지 않는다.
352
+ - desktop app: app.cxx의 ensureProcessServiceFactory()에서 수행
353
+ - unopkg: unopkg_misc.cxx의 bootstrapStandAlone()에서 함께 수행.
354
+
355
+
356
+ * desktop app: desktop/source/app/
357
+ * unopkg: desktop/source/pkgchk/unopkg/
358
+
359
+ '''
360
+
361
+ logger.info('%s: end of file', __name__)
tools/oxt.tool/oxt_tool/description.py ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import with_statement
3
+ import logging
4
+
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ NS_URI = 'http://openoffice.org/extensions/description/2006'
10
+ NS_URI_DEP = 'http://openoffice.org/extensions/description/2006'
11
+ NS_URI_XLINK = 'http://www.w3.org/1999/xlink'
12
+
13
+ NS = '{' + NS_URI + '}'
14
+ NS_DEP = '{' + NS_URI_DEP + '}'
15
+ NS_XLINK = '{' + NS_URI_XLINK + '}'
16
+
17
+
18
+ def as_dict(f):
19
+ def wrapper(*args, **kwargs):
20
+ return dict(f(*args, **kwargs))
21
+ wrapper.items = f
22
+ return wrapper
23
+
24
+
25
+ @as_dict
26
+ def get_display_name(doc):
27
+ root = doc.getroot()
28
+ for elt in root.findall(NS + 'display-name/' + NS + 'name'):
29
+ yield elt.get('lang'), elt.text
30
+
31
+
32
+ def set_display_name(doc, display_name):
33
+ import xml.etree.ElementTree as ET
34
+ root = doc.getroot()
35
+ dispname = ET.SubElement(root, 'display-name')
36
+ for lang, name in display_name.items():
37
+ elt = ET.SubElement(dispname, 'name')
38
+ elt.set('lang', lang)
39
+ elt.text = name
40
+
41
+
42
+ @as_dict
43
+ def get_extension_description(doc):
44
+ root = doc.getroot()
45
+ for elt in root.findall(NS + 'extension-description/' + NS + 'src'):
46
+ yield elt.get('lang'), elt.get(NS_XLINK + 'href')
47
+
48
+
49
+ def set_extension_description(doc, description):
50
+ import xml.etree.ElementTree as ET
51
+ root = doc.getroot()
52
+ desc = ET.SubElement(root, 'extension-description')
53
+ for lang, url in description.items():
54
+ elt = ET.SubElement(desc, 'src')
55
+ elt.set('lang', lang)
56
+ elt.set('xlink:href', url)
57
+
58
+
59
+ @as_dict
60
+ def get_publisher(doc):
61
+ root = doc.getroot()
62
+ for elt in root.findall(NS + 'publisher/' + NS + 'name'):
63
+ yield elt.get('lang'), dict(name=elt.text,
64
+ url=elt.get(NS_XLINK + 'href'))
65
+
66
+
67
+ def set_publisher(doc, publisher):
68
+ import xml.etree.ElementTree as ET
69
+ root = doc.getroot()
70
+ pub = ET.SubElement(root, 'publisher')
71
+ for lang, dct in publisher.items():
72
+ elt = ET.SubElement(pub, 'name')
73
+ elt.set('lang', lang)
74
+ elt.set('xlink:href', dct['url'])
75
+ elt.text = dct['name']
76
+
77
+
78
+ def get_license_accept_by(doc):
79
+ root = doc.getroot()
80
+ for elt in root.findall(NS + 'registration/' + NS + 'simple-license'):
81
+ accept_by = elt.get('accept-by')
82
+ if accept_by:
83
+ return accept_by
84
+
85
+
86
+ def get_license(doc):
87
+ root = doc.getroot()
88
+ for elt in root.findall(NS + 'registration/' + NS + 'simple-license'):
89
+ return dict((elt.get('lang'), elt.get(NS_XLINK + 'href'))
90
+ for elt in elt.findall(NS + 'license-text'))
91
+ return dict()
92
+
93
+
94
+ def get_oo_min_version(doc):
95
+ root = doc.getroot()
96
+ for dep in root.findall(NS + 'dependencies'):
97
+ for elt in dep.findall(NS + 'OpenOffice.org-minimal-version'):
98
+ return elt.get('value')
99
+
100
+
101
+ class Description(object):
102
+
103
+ @classmethod
104
+ def parse(cls, f):
105
+ import xml.etree.ElementTree as ET
106
+
107
+ doc = ET.parse(f)
108
+ root = doc.getroot()
109
+
110
+ def getvalue(xpath, default=None):
111
+ for elt in root.findall(xpath):
112
+ value = elt.get('value', default)
113
+ if value:
114
+ return value
115
+ return default
116
+
117
+ return cls(identifier=getvalue(NS + 'identifier'),
118
+ version=getvalue(NS + 'version'),
119
+ platform=getvalue(NS + 'platform', 'all'),
120
+ display_name=get_display_name(doc),
121
+ description=get_extension_description(doc),
122
+ publisher=get_publisher(doc),
123
+ license_accept_by=get_license_accept_by(doc),
124
+ license=get_license(doc),
125
+ oo_min_version=get_oo_min_version(doc))
126
+
127
+ def __init__(self,
128
+ identifier='noname',
129
+ version='0.0',
130
+ platform='all',
131
+ display_name=dict(),
132
+ description=dict(),
133
+ publisher=dict(),
134
+ license_accept_by='admin',
135
+ license=dict(),
136
+ oo_min_version=None):
137
+ ''' Generate description.xml
138
+
139
+ :param f: output file
140
+ :param identifier: extension identifier
141
+ :param version: extension version
142
+ :param platform: target platform
143
+ :param display_name: localizations of display name
144
+ :param description: localizations of extension description
145
+ :param publisher: localizations of publisher
146
+ :param license_accept_by: who is supposed to accept the license
147
+ :param license: localization of license
148
+ :param oo_min_version: minimal version of LibreOffice
149
+
150
+ Each localization parameters are dicts, whose keys are language identifiers
151
+ defined in RFC 3066.
152
+
153
+ ``identifier`` specifies `Extension Identifier
154
+ <http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extension_Identifiers>`_.
155
+
156
+ ``version`` specifies `Extension Version
157
+ <http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extension_Versions>`_.
158
+
159
+ ``platform`` specifies supposed `Target Platform
160
+ <http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Target_Platform>`_ on which this extension
161
+ runs. Default value is ``all``.
162
+
163
+ ``display_name`` specifies localized `Display Names
164
+ <http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Display_Name>`_.
165
+ It's a localization dict whose values are localized unicode strings, e.g.::
166
+
167
+ display_name = {
168
+ 'en': 'Example Filter',
169
+ 'ko': u'예제 필터'
170
+ }
171
+
172
+ Values of ``description`` is a URL of description file, e.g.::
173
+
174
+ description = {
175
+ 'en': 'description/en.txt',
176
+ 'ko': 'description/ko.txt'
177
+ }
178
+
179
+ ``publisher`` specifies `Publisher Information
180
+ <http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Publisher_Information>`_.
181
+ It's a localization dict whose values are dicts themselves, which have
182
+ ``name`` and ``url``. ``name`` is a localized name of the publisher and
183
+ ``url`` is a URL of the publisher. For example::
184
+
185
+ publisher = {
186
+ 'en': {
187
+ 'name': 'John Doe',
188
+ 'url': 'http://example.tld'
189
+ },
190
+ 'ko': {
191
+ 'name': u'홍길동',
192
+ 'url': 'http://example.tld'
193
+ }
194
+ }
195
+
196
+ Optional ``license_accept_by`` specifies who is supposed to accept the
197
+ license. ``admin`` or ``user``. Default value is 'admin'.
198
+
199
+ Optional ``license`` is a localization dict whose values are an URL of
200
+ license file. For example::
201
+
202
+ license = {
203
+ 'en': 'registration/COPYING'
204
+ }
205
+
206
+ See `Simple License
207
+ <http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Simple_License>`_.
208
+ '''
209
+ self.identifier = identifier
210
+ self.version = version
211
+ self.platform = platform
212
+ self.display_name = display_name
213
+ self.description = description
214
+ self.publisher = publisher
215
+ self.license_accept_by = license_accept_by
216
+ self.license = license
217
+ self.oo_min_version = oo_min_version
218
+
219
+ def write(self, f):
220
+
221
+ # see http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Description_of_XML_Elements
222
+
223
+ import xml.etree.ElementTree as ET
224
+
225
+ root = ET.Element('description', {'xmlns': NS_URI,
226
+ 'xmlns:dep': NS_URI_DEP,
227
+ 'xmlns:xlink': NS_URI_XLINK})
228
+ doc = ET.ElementTree(root)
229
+
230
+ ET.SubElement(root, 'identifier').set('value', self.identifier)
231
+ ET.SubElement(root, 'version').set('value', self.version)
232
+ ET.SubElement(root, 'platform').set('value', self.platform)
233
+
234
+ set_display_name(doc, self.display_name)
235
+
236
+ set_extension_description(doc, self.description)
237
+
238
+ set_publisher(doc, self.publisher)
239
+
240
+ if self.license:
241
+ reg = ET.SubElement(root, 'registration')
242
+ lic = ET.SubElement(reg, 'simple-license')
243
+ lic.set('accept-by', self.license_accept_by)
244
+ for lang, url in self.license.items():
245
+ elt = ET.SubElement(lic, 'license-text')
246
+ elt.set('lang', lang)
247
+ elt.set('xlink:href', url)
248
+
249
+ if self.oo_min_version is not None:
250
+ dep = ET.SubElement(root, 'dependencies')
251
+ minver = ET.SubElement(dep, 'OpenOffice.org-minimal-version')
252
+ minver.set('dep:name', 'LibreOffice ' + self.oo_min_version)
253
+ minver.set('value', self.oo_min_version)
254
+
255
+ f.write('<?xml version="1.0" encoding="utf-8"?>')
256
+ doc.write(f, encoding='utf-8')
257
+
258
+ def required_files(self):
259
+ for url in self.description.values():
260
+ yield url
261
+ for url in self.license.values():
262
+ yield url
263
+
264
+
265
+ def print_human_readable(desc, root_stg=None):
266
+ ''' Print summary in human readable form.
267
+
268
+ :param desc: an instance of Description
269
+ :param root_stg: root storage of description.xml
270
+ '''
271
+ from storage import resolve_path
272
+ print 'identifier:', desc.identifier
273
+ print 'version:', desc.version
274
+ print 'platform:', desc.platform
275
+
276
+ print 'display-name:'
277
+ for lang, name in desc.display_name.items():
278
+ print ' [%s] %s' % (lang, name)
279
+
280
+ print 'extension-description:'
281
+ for lang, url in desc.description.items():
282
+ if not root_stg or resolve_path(root_stg, url):
283
+ state = ''
284
+ else:
285
+ state = ' -- MISSING'
286
+ print ' [%s] %s%s' % (lang, url, state)
287
+
288
+ print 'publisher:'
289
+ for lang, publisher in desc.publisher.items():
290
+ print ' [%s] %s (%s)' % (lang,
291
+ publisher['name'],
292
+ publisher['url'])
293
+ if desc.license:
294
+ print 'license: accept-by', desc.license_accept_by
295
+ for lang, url in desc.license.items():
296
+ if not root_stg or resolve_path(root_stg, url):
297
+ state = ''
298
+ else:
299
+ state = ' -- MISSING'
300
+ print ' [%s] %s%s' % (lang, url, state)
301
+
302
+ if desc.oo_min_version:
303
+ print 'dependencies:'
304
+ print ' LibreOffice minimal version:', desc.oo_min_version
305
+
306
+
307
+ def init_main():
308
+ doc = '''Usage: oxt-desc-init [options] <desc-file>
309
+
310
+ --help Print this screen.
311
+ '''
312
+
313
+ from docopt import docopt
314
+ args = docopt(doc)
315
+ logging.basicConfig(level=logging.INFO)
316
+
317
+ description = Description(identifier='tld.example',
318
+ version='0.1',
319
+ display_name=dict(en='Example extension'),
320
+ publisher=dict(en=dict(name='Publisher Name',
321
+ url='http://example.tld')),
322
+ license=dict(url=dict(en='COPYING')),
323
+ description=dict(en='description/en.txt'))
324
+ with file(args['<desc-file>'], 'w') as f:
325
+ description.write(f)
326
+
327
+
328
+ def show_main():
329
+ doc = '''Usage: oxt-desc-show [options] <desc-file>
330
+
331
+ --help Show this screen.
332
+ '''
333
+ from docopt import docopt
334
+ args = docopt(doc)
335
+ logging.basicConfig(level=logging.INFO)
336
+
337
+ with file(args['<desc-file>']) as f:
338
+ desc = Description.parse(f)
339
+
340
+ print_human_readable(desc)
341
+
342
+
343
+ def version_main():
344
+ doc = '''Usage: oxt-desc-version [options] <desc-file> [<new-version>]
345
+
346
+ --help Show this screen.
347
+ '''
348
+ from docopt import docopt
349
+ args = docopt(doc)
350
+ logging.basicConfig(level=logging.INFO)
351
+
352
+ with file(args['<desc-file>'], 'r') as f:
353
+ desc = Description.parse(f)
354
+
355
+ new_version = args['<new-version>']
356
+ if new_version is not None:
357
+ logger.info('old: %s', desc.version)
358
+ desc.version = new_version
359
+ logger.info('new: %s', desc.version)
360
+ with file(args['<desc-file>'], 'w') as f:
361
+ desc.write(f)
362
+ else:
363
+ print desc.version
364
+
365
+
366
+ def ls_main():
367
+ doc = '''Usage: oxt-desc-ls [options] <desc-file>
368
+
369
+ --help Show this screen.
370
+ '''
371
+ from docopt import docopt
372
+ args = docopt(doc)
373
+ logging.basicConfig(level=logging.INFO)
374
+
375
+ with file(args['<desc-file>']) as f:
376
+ desc = Description.parse(f)
377
+
378
+ for path in desc.required_files():
379
+ print path