2015-04-27 14:31:25 -07:00
|
|
|
#! py -3
|
|
|
|
"""
|
2015-04-29 23:17:37 -07:00
|
|
|
Extract code examples from TIJ4 Refreshed. Extracts from plain text file.
|
|
|
|
Creates Ant build.xml file for each subdirectory.
|
2015-04-27 14:31:25 -07:00
|
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import sys, os
|
|
|
|
import re
|
|
|
|
import shutil
|
|
|
|
import pprint
|
2015-04-29 12:53:35 -07:00
|
|
|
import difflib
|
2015-05-15 22:36:55 -07:00
|
|
|
from collections import defaultdict
|
|
|
|
from betools import CmdLine, visitDir, ruler, head
|
2015-04-29 12:53:35 -07:00
|
|
|
|
2015-05-14 15:23:07 -07:00
|
|
|
maindef = re.compile("public\s+static\s+void\s+main")
|
|
|
|
|
2015-04-27 14:31:25 -07:00
|
|
|
destination = Path('.') / "ExtractedExamples"
|
|
|
|
sourceText = Path('.') / "TIJDirectorsCut.txt"
|
|
|
|
github = Path(r'C:\Users\Bruce\Documents\GitHub\TIJ-Directors-Cut')
|
|
|
|
|
|
|
|
startBuild = """\
|
|
|
|
<?xml version="1.0" ?>
|
|
|
|
|
|
|
|
<project default="run">
|
|
|
|
<property name="chapter" value="%s"/>
|
|
|
|
<property name="excludedfiles" value="%s"/>
|
|
|
|
<import file="../Ant-Common.xml"/>
|
2015-05-14 15:23:07 -07:00
|
|
|
<import file="../Ant-Clean.xml"/>
|
2015-04-27 14:31:25 -07:00
|
|
|
|
2015-04-27 21:27:42 -07:00
|
|
|
<target name="run" description="Compile and run" depends="build">
|
2015-04-27 14:31:25 -07:00
|
|
|
"""
|
|
|
|
|
|
|
|
endBuild = """\
|
|
|
|
</target>
|
|
|
|
|
|
|
|
</project>
|
|
|
|
"""
|
|
|
|
|
|
|
|
def extractExamples():
|
|
|
|
if not destination.exists():
|
|
|
|
destination.mkdir()
|
|
|
|
if not sourceText.exists():
|
|
|
|
print("Cannot find", sourceText)
|
|
|
|
sys.exit()
|
|
|
|
with sourceText.open("rb") as book:
|
|
|
|
text = book.read().decode("utf-8", "ignore")
|
2015-05-01 15:32:45 -07:00
|
|
|
for listing in (re.findall("^//:.*?///:~", text, re.DOTALL | re.MULTILINE) +
|
|
|
|
re.findall("^#:.*?#:~", text, re.DOTALL | re.MULTILINE)):
|
2015-04-27 14:31:25 -07:00
|
|
|
title = listing.splitlines()[0]
|
|
|
|
if "//: as a special marker" in title:
|
|
|
|
continue
|
|
|
|
title = title.split()[1]
|
|
|
|
print(title)
|
|
|
|
target = destination / Path(title)
|
|
|
|
if not target.parent.exists():
|
|
|
|
target.parent.mkdir(parents=True)
|
2015-05-02 15:37:54 -07:00
|
|
|
if "//:!" in listing:
|
|
|
|
listing = "".join(listing.splitlines(keepends=True)[1:-1])
|
2015-04-27 14:31:25 -07:00
|
|
|
with target.open("w", newline='') as codeListing:
|
2015-05-02 15:37:54 -07:00
|
|
|
codeListing.write(listing)
|
2015-04-30 12:21:26 -07:00
|
|
|
codeListing.write("\n")
|
2015-04-27 14:31:25 -07:00
|
|
|
|
2015-05-18 23:05:20 -07:00
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine("x")
|
2015-04-27 14:31:25 -07:00
|
|
|
def clean():
|
2015-05-12 14:02:20 -07:00
|
|
|
"Remove ExtractedExamples directory"
|
2015-04-27 14:31:25 -07:00
|
|
|
print("clean")
|
|
|
|
if destination.exists():
|
|
|
|
shutil.rmtree(str(destination))
|
|
|
|
|
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine("c")
|
2015-04-29 12:53:35 -07:00
|
|
|
def compareWithGithub(shortForm=True):
|
2015-05-12 14:02:20 -07:00
|
|
|
"Compare files from Github repository to extracted examples"
|
2015-04-27 14:31:25 -07:00
|
|
|
leader = len(str(github)) + 1
|
|
|
|
githubfiles = [str(file)[leader:] for file in github.glob("**/*")]
|
|
|
|
githubfiles = [ghf for ghf in githubfiles if not ghf.startswith(".git")]
|
|
|
|
duplicates = { ghf for ghf in githubfiles if githubfiles.count(ghf) > 1 }
|
2015-04-27 21:27:42 -07:00
|
|
|
if duplicates:
|
|
|
|
print("duplicates = ", duplicates)
|
2015-04-27 14:31:25 -07:00
|
|
|
|
|
|
|
leader2 = len(str(destination)) + 1
|
|
|
|
destfiles = [str(file)[leader2:] for file in destination.glob("**/*")]
|
|
|
|
duplicates = { ghf for ghf in destfiles if destfiles.count(ghf) > 1 }
|
2015-04-27 21:27:42 -07:00
|
|
|
if duplicates:
|
|
|
|
print("duplicates = ", duplicates)
|
2015-04-27 14:31:25 -07:00
|
|
|
|
2015-05-16 11:46:30 -07:00
|
|
|
githubfiles = set(githubfiles)
|
|
|
|
destfiles = set(destfiles)
|
2015-04-29 12:53:35 -07:00
|
|
|
|
|
|
|
runOutput = re.compile("/\* Output:.*///:~", re.DOTALL)
|
|
|
|
differ = difflib.Differ()
|
|
|
|
|
|
|
|
def rstrip(lines):
|
|
|
|
return [line.rstrip() for line in lines]
|
|
|
|
|
|
|
|
def show(lines, sep="#"):
|
|
|
|
sys.stdout.writelines(lines)
|
|
|
|
print("\n" + sep * 80)
|
|
|
|
|
|
|
|
|
|
|
|
inBoth = [f for f in destfiles.intersection(githubfiles) if f.endswith(".java")]
|
|
|
|
for f in inBoth:
|
|
|
|
with (github / f).open() as ghf:
|
|
|
|
with (destination / f).open() as dstf:
|
|
|
|
ghblock = runOutput.sub("", ghf.read())
|
|
|
|
dstblock = runOutput.sub("", dstf.read())
|
|
|
|
if ghblock.strip() == dstblock.strip():
|
|
|
|
continue
|
|
|
|
ghtext = ghblock.splitlines(keepends=True)
|
|
|
|
dsttext = dstblock.splitlines(keepends=True)
|
|
|
|
print("[[[", f, "]]]")
|
|
|
|
if shortForm:
|
|
|
|
show([ln + "\n" for ln in difflib.context_diff(rstrip(ghtext), rstrip(dsttext))], sep="=")
|
|
|
|
else:
|
|
|
|
show([ln + "\n" for ln in differ.compare(rstrip(ghtext), rstrip(dsttext))], sep="=")
|
2015-04-27 14:31:25 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def githubDirs():
|
|
|
|
leader = len(str(github)) + 1
|
|
|
|
buildfiles = [str(file)[leader:] for file in github.glob("**/build.xml")]
|
|
|
|
return {str((github / f).parent)[leader:] for f in buildfiles}
|
|
|
|
|
|
|
|
|
|
|
|
def destDirs(pattern="**"):
|
|
|
|
leader = len(str(destination)) + 1
|
|
|
|
return {str(file)[leader:] for file in destination.glob(pattern)}
|
|
|
|
|
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine("a")
|
2015-05-02 23:49:25 -07:00
|
|
|
def copySupplementalFilesFromGithub():
|
2015-05-12 14:02:20 -07:00
|
|
|
"Copy ant build files from Github repository to extracted examples"
|
2015-05-05 14:00:45 -07:00
|
|
|
shutil.copy(str(github / "build.xml"), str(destination))
|
2015-04-27 14:31:25 -07:00
|
|
|
shutil.copy(str(github / "Ant-Common.xml"), str(destination))
|
2015-05-14 15:23:07 -07:00
|
|
|
shutil.copy(str(github / "Ant-Clean.xml"), str(destination))
|
2015-04-30 23:42:10 -07:00
|
|
|
for face in (github / "gui").glob("*.gif"):
|
|
|
|
shutil.copy(str(face), str(destination / "gui"))
|
2015-05-12 14:12:23 -07:00
|
|
|
# for verifier in ["OutputGenerator.py", "OutputVerifier.py"]:
|
|
|
|
# shutil.copy(str(github/verifier), str(destination))
|
2015-05-02 15:37:54 -07:00
|
|
|
patterns = destination / "patterns"
|
|
|
|
trash = patterns / "recycleap" / "Trash.dat"
|
|
|
|
shutil.copy(str(trash), str(patterns / "recycleb"))
|
|
|
|
shutil.copy(str(trash), str(patterns / "dynatrash"))
|
2015-04-27 14:31:25 -07:00
|
|
|
|
2015-04-28 14:17:58 -07:00
|
|
|
|
|
|
|
|
2015-04-28 10:54:50 -07:00
|
|
|
class CodeFileOptions(object):
|
|
|
|
"""docstring for CodeFileOptions"""
|
|
|
|
def __init__(self, codeFile):
|
2015-04-28 14:17:58 -07:00
|
|
|
"Should probably use regular expressions for parsing instead"
|
2015-04-28 10:54:50 -07:00
|
|
|
self.codeFile = codeFile
|
2015-04-29 17:17:02 -07:00
|
|
|
self.msg = ""
|
2015-04-28 10:54:50 -07:00
|
|
|
|
|
|
|
self.cmdargs = None
|
|
|
|
if "{Args:" in self.codeFile.code:
|
|
|
|
for line in self.codeFile.lines:
|
|
|
|
if "{Args:" in line:
|
2015-04-28 14:17:58 -07:00
|
|
|
self.cmdargs = line.split("{Args:")[1].strip()
|
|
|
|
self.cmdargs = self.cmdargs.rsplit("}", 1)[0]
|
2015-04-28 10:54:50 -07:00
|
|
|
|
|
|
|
self.runbyhand = "{RunByHand}" in self.codeFile.code
|
|
|
|
|
|
|
|
self.exclude = None
|
|
|
|
if "{CompileTimeError}" in self.codeFile.code:
|
|
|
|
self.exclude = self.codeFile.name + ".java"
|
|
|
|
if self.codeFile.subdirs:
|
|
|
|
self.exclude = '/'.join(self.codeFile.subdirs) + '/' + self.exclude
|
|
|
|
print(self.exclude)
|
|
|
|
|
2015-04-29 23:17:37 -07:00
|
|
|
self.continue_on_error = None
|
|
|
|
if "{ThrowsException}" in self.codeFile.code:
|
|
|
|
self.continue_on_error = True
|
|
|
|
self.msg = "* Exception was Expected *"
|
|
|
|
|
2015-04-28 10:54:50 -07:00
|
|
|
|
2015-04-28 14:17:58 -07:00
|
|
|
self.alternatemainclass = None
|
|
|
|
if "{main: " in self.codeFile.code:
|
|
|
|
for line in self.codeFile.lines:
|
|
|
|
if "{main:" in line:
|
|
|
|
self.alternatemainclass = line.split("{main:")[1].strip()
|
|
|
|
self.alternatemainclass = self.alternatemainclass.rsplit("}", 1)[0]
|
|
|
|
|
2015-04-29 12:53:35 -07:00
|
|
|
self.timeout = None
|
|
|
|
if "{TimeOut:" in self.codeFile.code:
|
|
|
|
for line in self.codeFile.lines:
|
|
|
|
if "{TimeOut:" in line:
|
|
|
|
self.timeout = line.split("{TimeOut:")[1].strip()
|
|
|
|
self.timeout = self.timeout.rsplit("}", 1)[0]
|
|
|
|
self.continue_on_error = True
|
2015-04-29 23:17:37 -07:00
|
|
|
elif "//: gui/" in self.codeFile.code or "//: swt/" in self.codeFile.code or "{TimeOutDuringTesting}" in self.codeFile.code:
|
|
|
|
self.timeout = "4000"
|
|
|
|
self.continue_on_error = True
|
|
|
|
self.msg = "* Timeout for Testing *"
|
|
|
|
|
2015-04-28 14:17:58 -07:00
|
|
|
def classFile(self):
|
|
|
|
start = """ <jrun cls="%s" """
|
|
|
|
if self.alternatemainclass:
|
|
|
|
return start % self.alternatemainclass
|
|
|
|
if self.codeFile.package:
|
|
|
|
return start % (self.codeFile.packageName() + '.' + self.codeFile.name)
|
|
|
|
return start % self.codeFile.name
|
|
|
|
|
|
|
|
def dirPath(self):
|
|
|
|
if self.codeFile.package:
|
|
|
|
return """dirpath="%s" """ % self.codeFile.relpath
|
|
|
|
return ""
|
|
|
|
|
|
|
|
def arguments(self):
|
|
|
|
if self.cmdargs:
|
2015-05-07 17:22:48 -07:00
|
|
|
return """arguments="%s" """ % self.cmdargs
|
2015-04-28 14:17:58 -07:00
|
|
|
return ""
|
|
|
|
|
|
|
|
def failOnError(self):
|
2015-04-29 12:53:35 -07:00
|
|
|
if self.continue_on_error:
|
2015-04-28 14:17:58 -07:00
|
|
|
return """failOnError='false' """
|
|
|
|
return ""
|
|
|
|
|
2015-04-29 12:53:35 -07:00
|
|
|
def timeOut(self):
|
|
|
|
if self.timeout:
|
|
|
|
return """timeOut='%s' """ % self.timeout
|
|
|
|
return ""
|
|
|
|
|
2015-04-29 17:17:02 -07:00
|
|
|
def message(self):
|
|
|
|
if self.msg:
|
|
|
|
return """msg='%s' """ % self.msg
|
|
|
|
return ""
|
|
|
|
|
2015-04-28 14:17:58 -07:00
|
|
|
def createRunCommand(self):
|
2015-05-12 14:12:23 -07:00
|
|
|
return self.classFile() + self.dirPath() + \
|
|
|
|
self.arguments() + self.failOnError() + \
|
|
|
|
self.timeOut() + self.message() + "/>\n"
|
2015-04-28 14:17:58 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-04-27 14:31:25 -07:00
|
|
|
class CodeFile:
|
2015-04-27 21:27:42 -07:00
|
|
|
def __init__(self, javaFile, chapterDir):
|
|
|
|
self.chapter_dir = chapterDir
|
|
|
|
self.java_file = javaFile
|
|
|
|
self.subdirs = str(javaFile.parent).split("\\")[2:]
|
2015-04-27 14:31:25 -07:00
|
|
|
with javaFile.open() as j:
|
|
|
|
self.code = j.read()
|
|
|
|
self.lines = self.code.splitlines()
|
|
|
|
self.main = None
|
2015-05-14 15:23:07 -07:00
|
|
|
if maindef.search(self.code):
|
2015-04-27 14:31:25 -07:00
|
|
|
self.main = True
|
|
|
|
self.package = None
|
|
|
|
if "package " in self.code:
|
|
|
|
for line in self.lines:
|
|
|
|
if line.startswith("package ") and line.strip().endswith(";"):
|
|
|
|
self.package = line
|
|
|
|
break
|
|
|
|
self.tagLine = self.lines[0][4:]
|
|
|
|
self.relpath = '../' + '/'.join(self.tagLine.split('/')[:-1])
|
|
|
|
self.name = javaFile.name.split('.')[0]
|
2015-04-28 10:54:50 -07:00
|
|
|
self.options = CodeFileOptions(self)
|
2015-04-28 00:00:38 -07:00
|
|
|
|
|
|
|
def run_command(self):
|
|
|
|
if not self.main:
|
|
|
|
return ""
|
2015-04-28 14:17:58 -07:00
|
|
|
return self.options.createRunCommand()
|
2015-04-27 14:31:25 -07:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
result = self.tagLine
|
|
|
|
if self.package:
|
|
|
|
result += "\n" + self.package
|
|
|
|
result += "\n"
|
|
|
|
return result
|
|
|
|
|
|
|
|
def packageName(self):
|
|
|
|
return self.package.split()[1][:-1]
|
|
|
|
|
|
|
|
def checkPackage(self):
|
|
|
|
if not self.package:
|
|
|
|
return True
|
|
|
|
path = '.'.join(self.tagLine.split('/')[:-1])
|
|
|
|
packagePath = self.packageName()
|
|
|
|
return path == packagePath
|
|
|
|
|
|
|
|
|
2015-04-28 14:17:58 -07:00
|
|
|
|
|
|
|
|
2015-04-27 14:31:25 -07:00
|
|
|
class Chapter:
|
|
|
|
def __init__(self, dir):
|
|
|
|
self.dir = dir
|
2015-04-27 21:27:42 -07:00
|
|
|
self.code_files = [CodeFile(javaFile, dir) for javaFile in dir.glob("**/*.java")]
|
2015-04-28 10:54:50 -07:00
|
|
|
self.excludes = [cf.options.exclude for cf in self.code_files if cf.options.exclude]
|
2015-04-27 14:31:25 -07:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
result = "-" * 80
|
|
|
|
result += "\n" + str(self.dir) + "\n"
|
|
|
|
result += "-" * 80
|
|
|
|
result += "\n"
|
|
|
|
for cf in self.code_files:
|
|
|
|
result += str(cf.name) + "\n"
|
|
|
|
return result
|
|
|
|
|
|
|
|
def checkPackages(self):
|
|
|
|
for cf in self.code_files:
|
|
|
|
if not cf.checkPackage():
|
|
|
|
print("BAD PACKAGE")
|
|
|
|
print("\t", cf.tagLine)
|
|
|
|
print("\t", cf.package)
|
|
|
|
print("\n".join(cf.lines))
|
|
|
|
|
|
|
|
def makeBuildFile(self):
|
|
|
|
buildFile = startBuild % (self.dir.name, " ".join(self.excludes))
|
|
|
|
for cf in self.code_files:
|
2015-04-28 10:54:50 -07:00
|
|
|
if any([cf.name + ".java" in f for f in self.excludes]) or cf.options.runbyhand:
|
2015-04-27 14:31:25 -07:00
|
|
|
continue
|
2015-04-28 00:00:38 -07:00
|
|
|
buildFile += cf.run_command()
|
2015-04-27 14:31:25 -07:00
|
|
|
buildFile += endBuild
|
|
|
|
with (self.dir / "build.xml").open("w") as buildxml:
|
|
|
|
buildxml.write(buildFile)
|
|
|
|
|
|
|
|
|
2015-04-28 14:17:58 -07:00
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine("m")
|
2015-04-27 14:31:25 -07:00
|
|
|
def createAntFiles():
|
2015-05-12 14:02:20 -07:00
|
|
|
"Make ant files that don't exist"
|
2015-04-27 14:31:25 -07:00
|
|
|
chapters = [Chapter(fd) for fd in destination.glob("*") if fd.is_dir() if not (fd / "build.xml").exists()]
|
|
|
|
for chapter in chapters:
|
|
|
|
chapter.checkPackages()
|
|
|
|
chapter.makeBuildFile()
|
|
|
|
|
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine("f")
|
2015-04-30 23:42:10 -07:00
|
|
|
def findNonJavaFiles():
|
2015-05-12 14:02:20 -07:00
|
|
|
"Find non-java files in TIJDirectorsCut.txt"
|
2015-04-30 23:42:10 -07:00
|
|
|
if not sourceText.exists():
|
|
|
|
print("Cannot find", sourceText)
|
|
|
|
sys.exit()
|
|
|
|
with sourceText.open("rb") as book:
|
|
|
|
text = book.read().decode("utf-8", "ignore")
|
|
|
|
for listing in re.findall("^//:.*?///:~", text, re.DOTALL | re.MULTILINE):
|
|
|
|
title = listing.splitlines()[0].strip()
|
|
|
|
if not title.endswith(".java"):
|
|
|
|
print(title)
|
|
|
|
|
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine('e')
|
2015-05-12 14:02:20 -07:00
|
|
|
def extractAndCreateBuildFiles():
|
2015-05-12 14:07:48 -07:00
|
|
|
"Clean, then extract examples from TIJDirectorsCut.txt, build ant files"
|
2015-04-27 14:31:25 -07:00
|
|
|
clean()
|
|
|
|
extractExamples()
|
2015-05-02 23:49:25 -07:00
|
|
|
copySupplementalFilesFromGithub()
|
2015-04-27 14:31:25 -07:00
|
|
|
createAntFiles()
|
|
|
|
os.chdir("ExtractedExamples")
|
2015-05-12 13:41:20 -07:00
|
|
|
with open("run.bat", 'w') as run:
|
|
|
|
run.write(r"python ..\Validate.py -p" + "\n")
|
|
|
|
run.write(r"powershell .\runall.ps1" + "\n")
|
2015-04-27 14:31:25 -07:00
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine('g')
|
|
|
|
def generateAntClean():
|
2015-05-14 15:23:07 -07:00
|
|
|
"Generate directives for Ant-Clean.xml"
|
|
|
|
examples = Path(r"C:\Users\Bruce\Dropbox\__TIJ4-ebook\ExtractedExamples")
|
|
|
|
others = set([f.name for f in examples.rglob("*") if not f.is_dir()
|
|
|
|
if not f.suffix == ".java"
|
|
|
|
if not f.suffix == ".class"
|
|
|
|
if not f.suffix == ".py"
|
|
|
|
if not f.suffix == ".cpp"
|
|
|
|
if not str(f).endswith("-output.txt")
|
|
|
|
if not str(f).endswith("-erroroutput.txt")
|
|
|
|
if f.name
|
|
|
|
])
|
|
|
|
for f in others:
|
|
|
|
print(""" <exclude name="**/{}" />""".format(f))
|
|
|
|
|
|
|
|
# pprint.pprint([f for f in others if "test" in f or "Test" in f])
|
|
|
|
|
2015-05-15 22:36:55 -07:00
|
|
|
tagRE = re.compile("{.*?}", re.DOTALL)
|
|
|
|
|
|
|
|
def findTags(lines):
|
|
|
|
topblock = []
|
|
|
|
for line in lines:
|
|
|
|
if line.startswith("//"):
|
|
|
|
topblock.append(line)
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
topblock = [line[2:].strip() for line in topblock]
|
|
|
|
tags = tagRE.findall(" ".join(topblock))
|
|
|
|
return tags
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-05-16 14:10:17 -07:00
|
|
|
@CmdLine('t')
|
2015-05-15 22:36:55 -07:00
|
|
|
def findAllCommentTags():
|
|
|
|
"Find all '{}' comment tags in Java files"
|
|
|
|
tagdict = defaultdict(list)
|
|
|
|
examples = Path(r"C:\Users\Bruce\Dropbox\__TIJ4-ebook\ExtractedExamples")
|
|
|
|
for jf in [f for f in examples.rglob("*.java")]:
|
|
|
|
with jf.open() as code:
|
|
|
|
lines = code.readlines()
|
|
|
|
tags = findTags(lines)
|
|
|
|
if tags:
|
|
|
|
# head(jf.name)
|
|
|
|
# print("\n".join(tags))
|
|
|
|
for t in tags:
|
|
|
|
tagdict[t].append(jf.name)
|
|
|
|
pprint.pprint(tagdict)
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-05-12 14:02:20 -07:00
|
|
|
if __name__ == '__main__': CmdLine.run()
|