Skip to content

Method: doWork()

1: package de.fhdw.wtf.generator.transformer.clipper;
2:
3: import java.io.File;
4: import java.io.IOException;
5: import java.util.ArrayList;
6: import java.util.Iterator;
7: import java.util.List;
8: import java.util.regex.Matcher;
9: import java.util.regex.Pattern;
10:
11: import de.fhdw.wtf.common.exception.walker.TaskException;
12: import de.fhdw.wtf.common.task.DependencyTask;
13: import de.fhdw.wtf.common.task.TaskExecutor;
14: import de.fhdw.wtf.common.task.result.ExceptionalTaskResult;
15: import de.fhdw.wtf.common.task.result.OKTaskResult;
16: import de.fhdw.wtf.common.task.result.TaskResult;
17: import de.fhdw.wtf.file.FileUtils;
18: import de.fhdw.wtf.generator.transformer.clipper.internal.ClipperJavaFileShadowCopy;
19: import de.fhdw.wtf.generator.transformer.exception.ClipperImportFormatException;
20: import de.fhdw.wtf.generator.transformer.util.Tuple;
21:
22: /**
23: * Extracts methods from current java project into Clipper repository.
24: *
25: */
26: public class ClipToFileTask extends DependencyTask {
27:         
28:         /**
29:          * Constant for "}".
30:          */
31:         private static final String CURLY_BRACKET_CLOSE = "}";
32:         
33:         /**
34:          * Constant for "{".
35:          */
36:         private static final String CURLY_BRACKET_OPEN = "{";
37:         
38:         /**
39:          * Constant for "/".
40:          */
41:         private static final String FILESYSTEM_PATH_SEPERATOR = "/";
42:         
43:         /**
44:          * Constant for "\\".
45:          */
46:         private static final String BRACKET_CLOSE = "\\)";
47:         
48:         /**
49:          * Constant for "\\(".
50:          */
51:         private static final String BRACKET_OPEN = "\\(";
52:         
53:         /**
54:          * The associated clipper configuration.
55:          */
56:         private final ClipperConfiguration configuration;
57:         
58:         /**
59:          * This task extracts methods, attributes and comments from the Java source code into the Clipper repository.
60:          *
61:          * @param taskmanager
62:          * manager that e.g. starts this task when its dependencies are met
63:          * @param configuration
64:          * the configuration
65:          */
66:         public ClipToFileTask(final TaskExecutor taskmanager, final ClipperConfiguration configuration) {
67:                 super(taskmanager);
68:                 this.configuration = configuration;
69:         }
70:         
71:         @Override
72:         public boolean containsTransitive(final DependencyTask a) {
73:                 return false;
74:         }
75:         
76:         @Override
77:         public TaskResult doWork() {
78:                 try {
79:                         FileUtils.deleteDirectory(this.configuration.getRepoDirectoryRoot());
80:                         this.walkThroughJava(new File(this.configuration.getJavaDirectoryRoot()));
81:                 } catch (final TaskException | IOException e) {
82:                         return new ExceptionalTaskResult(e);
83:                 }
84:                 return new OKTaskResult();
85:         }
86:         
87:         private void walkThroughJava(final File startDir) throws TaskException, IOException {
88:                 final File[] directoryListing = startDir.listFiles();
89:                 if (directoryListing != null) {
90:                         for (final File child : directoryListing) {
91:                                 if (child.isDirectory()) {
92:                                         this.walkThroughJava(child);
93:                                 } else {
94:                                         String fullClassName = child.getCanonicalFile().getCanonicalPath();
95:                                         fullClassName =
96:                                                         fullClassName.substring(new File(this.configuration.getJavaDirectoryRoot())
97:                                                                         .getCanonicalPath().length() + 1);
98:                                         final File classDir = this.createDirectoryForClass(fullClassName);
99:                                         try {
100:                                                 this.scanFileToDirectory(child, classDir);
101:                                         } catch (final ClipperImportFormatException | TaskException e) {
102:                                                 System.err.println("ClipperImportFormatException(" + fullClassName + "): " + e.getMessage());
103:                                                 // continue with next file!
104:                                                 // throw new TaskException(e); //enabled in test TODO
105:                                         }
106:                                         
107:                                 }
108:                                 
109:                         }
110:                 } else {
111:                         System.err.println("ClipperImportFormatException(" + startDir + "): Java Directory cannot be accessed.");
112:                 }
113:         }
114:         
115:         private void scanFileToDirectoryInternal(final File path, final ClipperJavaFileShadowCopy javaFile)
116:                         throws IOException, ClipperImportFormatException {
117:                 final File classRepoDir = path;
118:                 
119:                 // Erzeuge Unterordner wenn notwendig
120:                 // --------------------------------------------------------------------------
121:                 if (!classRepoDir.exists()) {
122:                         if (!classRepoDir.mkdir()) {
123:                                 throw new ClipperImportFormatException("Ordner '" + classRepoDir + "' konnte nicht erzeugt werden!");
124:                         }
125:                 }
126:                 
127:                 // Iteriere über alle inneren Klassen
128:                 // --------------------------------------------------------------------------
129:                 final Iterator<ClipperJavaFileShadowCopy> it = javaFile.getInnerClasses().iterator();
130:                 while (it.hasNext()) {
131:                         final ClipperJavaFileShadowCopy current = it.next();
132:                         final String newPath = path + "/" + current.getName();
133:                         this.scanFileToDirectoryInternal(new File(newPath), current);
134:                 }
135:                 
136:                 // Hier startet der eigentliche Clipper
137:                 // --------------------------------------------------------------------------
138:                 String file = javaFile.getFullClass();
139:                 file = this.extractNonGenerationPart(classRepoDir, file);
140:                 final String withoutcommentFile = ClipperUtils.simplifyJavaCode(file);
141:                 
142:                 // Klammern ermitteln
143:                 final int classStart = this.extractClassComment(classRepoDir, file, withoutcommentFile);
144:                 final int classEnd = findCorrespondingClosingCurlyBracket(classStart, withoutcommentFile);
145:                 
146:                 // Schneidet den Inhalt der Klasse heraus (= entfernen der Klammern & Co.)
147:                 final String cFile = file.substring(classStart + 1, classEnd);
148:                 final String cWithoutComments = withoutcommentFile.substring(classStart + 1, classEnd);
149:                 
150:                 int pos = 0;
151:                 while (true) {
152:                         int i = cWithoutComments.indexOf(String.valueOf(';'), pos);
153:                         if (i == -1) {
154:                                 break;
155:                         }
156:                         i++;
157:                         final String opOrAttWithout = cWithoutComments.substring(pos, i);
158:                         String opOrAttFile = cFile.substring(pos, i);
159:                         opOrAttFile = this.removeLeadingWhiteBreaks(opOrAttFile);
160:                         if (opOrAttWithout.contains("(")) {
161:                                 this.parseOperationToFile(opOrAttWithout, opOrAttFile, classRepoDir);
162:                         } else {
163:                                 this.parseAttributeToFile(opOrAttWithout, opOrAttFile, classRepoDir);
164:                         }
165:                         pos = i;
166:                 }
167:                 int importsBegin = 0;
168:                 int importsEnd = 0;
169:                 importsBegin = file.indexOf("import ", importsBegin);
170:                 
171:                 if (importsBegin >= 0) {
172:                         int importBegin = importsBegin;
173:                         while (importBegin >= 0) {
174:                                 importsEnd = file.indexOf(';', importBegin);
175:                                 importBegin = file.indexOf("import", importsEnd);
176:                         }
177:                         importsEnd++;
178:                         final String preCFile = file.substring(0, importsEnd);
179:                         
180:                         final String importsBlockWithout = preCFile.substring(importsBegin, importsEnd);
181:                         final String importsBlockInFile = preCFile.substring(importsBegin, importsEnd);
182:                         this.parseImportsToFile(importsBlockWithout, importsBlockInFile, classRepoDir);
183:                 }
184:         }
185:         
186:         /**
187:          * Scans the given class file operations to the local clipper repository.
188:          *
189:          * @param classFile
190:          * @param classRepoDir
191:          * @throws IOException
192:          * @throws TaskException
193:          * @throws ClipperImportFormatException
194:          */
195:         private void scanFileToDirectory(final File classFile, final File classRepoDir) throws IOException, TaskException,
196:                         ClipperImportFormatException {
197:                 
198:                 // Load the source into "file"
199:                 final String file = FileUtils.getFileString(classFile.getAbsolutePath());
200:                 final ClipperJavaFileShadowCopy clss = new ClipperJavaFileShadowCopy();
201:                 clss.analyze(file);
202:                 
203:                 // Analyze
204:                 this.scanFileToDirectoryInternal(classRepoDir, clss);
205:                 
206:         }
207:         
208:         /**
209:          *
210:          * @param opOrAttFile
211:          * String to be bettered
212:          * @return better String
213:          */
214:         private String removeLeadingWhiteBreaks(final String opOrAttFile) {
215:                 String result = opOrAttFile;
216:                 boolean lastRunWithoutChanges = false;
217:                 while (!lastRunWithoutChanges) {
218:                         lastRunWithoutChanges = true;
219:                         if (result.startsWith("\t\n")) {
220:                                 result = result.replaceFirst("\t\n", "");
221:                                 lastRunWithoutChanges = false;
222:                         }
223:                         if (result.startsWith("\n")) {
224:                                 result = result.replaceFirst("\n", "");
225:                                 lastRunWithoutChanges = false;
226:                         }
227:                         if (result.startsWith("\t\t")) {
228:                                 result = result.replaceFirst("\t\t", "\t");
229:                                 lastRunWithoutChanges = false;
230:                         }
231:                 }
232:                 return result;
233:         }
234:         
235:         /**
236:          *
237:          * @param withoutcomments
238:          * probably needed for further changes
239:          * @param file
240:          * that will be saved to directory
241:          * @param classRepoDir
242:          * path of directory
243:          * @throws IOException
244:          * saveToFile may throw this exception
245:          */
246:         private void parseImportsToFile(final String withoutcomments, final String file, final File classRepoDir)
247:                         throws IOException {
248:                 FileUtils.saveToFile(file, new File(classRepoDir.getAbsolutePath() + FILESYSTEM_PATH_SEPERATOR
249:                                 + ClipperUtils.IMPORTS_FILENAME));
250:                 
251:         }
252:         
253:         private void parseAttributeToFile(final String withoutcomments, final String file, final File classRepoDir)
254:                         throws IOException {
255:                 final String opPattern = "\\s*" + "(?<type>.*)?\\s+" + "(?<name>" + ClipperUtils.IDENTIFIER_REGEX + ")\\s*;";
256:                 final Pattern p = Pattern.compile(opPattern, Pattern.DOTALL);
257:                 final Matcher m = p.matcher(withoutcomments);
258:                 if (m.matches()) {
259:                         final String name = m.group("name");
260:                         final String type = replaceIllegalChars(m.group("type"));
261:                         final String filename = ClipperUtils.ATTRIBUTE_START + name + ClipperUtils.SEPERATOR + type;
262:                         FileUtils.saveToFile(file, new File(classRepoDir.getAbsolutePath() + FILESYSTEM_PATH_SEPERATOR + filename));
263:                         
264:                 }
265:         }
266:         
267:         /**
268:          * "Repairs" a parameter type list by gluing parts that belong to the same parameter type. This is necessary as a
269:          * type parameter may have generic type arguments which may also be separated by ",".
270:          *
271:          * @param params
272:          * The parameter type list resulting from splitting at ",".
273:          * @return The repaired type list.
274:          */
275:         private List<String> repairGenerics(final String[] params) {
276:                 final List<String> result = new ArrayList<>();
277:                 int angleBracketsDifference = 0;
278:                 for (final String param : params) {
279:                         if (angleBracketsDifference != 0) {
280:                                 result.set(result.size() - 1, result.get(result.size() - 1) + ClipperUtils.PARAMETER_SEPARATOR + param);
281:                         } else {
282:                                 result.add(param);
283:                         }
284:                         angleBracketsDifference +=
285:                                         param.split(ClipperUtils.GENERIC_OPEN).length - param.split(ClipperUtils.GENERIC_CLOSE).length;
286:                 }
287:                 return result;
288:         }
289:         
290:         private void parseOperationToFile(final String withoutcomments, final String file, final File classRepoDir)
291:                         throws ClipperImportFormatException, IOException {
292:                 final String opPattern =
293:                                 "\\s*" + "(?<type>.*\\s+)?" + "(?<name>" + ClipperUtils.IDENTIFIER_REGEX + ")\\s*" + BRACKET_OPEN
294:                                                 + "(?<params>.*)" + BRACKET_CLOSE + ".*\\s*;";
295:                 final Pattern p = Pattern.compile(opPattern, Pattern.DOTALL);
296:                 final Matcher m = p.matcher(withoutcomments);
297:                 if (m.matches()) {
298:                         String name = m.group("name");
299:                         final String type = m.group("type");
300:                         String params = m.group("params");
301:                         
302:                         if (type == null) {
303:                                 name = ClipperUtils.CONSTRUCTOR_FILENAME;
304:                         }
305:                         if (params.trim().length() > 0) {
306:                                 params += ClipperUtils.PARAMETER_SEPARATOR;
307:                                 final String[] paramStrings = params.split(ClipperUtils.PARAMETER_SEPARATOR);
308:                                 final String nextWordPattern = "\\s*(.*)\\s+(" + ClipperUtils.IDENTIFIER_REGEX + ")\\s*";
309:                                 final Pattern p1 = Pattern.compile(nextWordPattern, Pattern.DOTALL);
310:                                 for (final String para : this.repairGenerics(paramStrings)) {
311:                                         final String trimmedPara = para.trim();
312:                                         final Matcher m1 = p1.matcher(trimmedPara);
313:                                         if (m1.matches()) {
314:                                                 final String paramType = trimmedPara.substring(m1.start(1), m1.end(1));
315:                                                 name += ClipperUtils.SEPERATOR + paramType;
316:                                         } else {
317:                                                 throw new ClipperImportFormatException("Parameter seems invalid: " + params);
318:                                         }
319:                                 }
320:                         }
321:                         
322:                         if (!name.contains(ClipperUtils.CONSTRUCTOR_FILENAME)) {
323:                                 name = replaceIllegalChars(name);
324:                                 if (!name.startsWith("$super")) {
325:                                         FileUtils.saveToFile(file, new File(classRepoDir.getAbsolutePath() + FILESYSTEM_PATH_SEPERATOR
326:                                                         + name));
327:                                 }
328:                         }
329:                 } else {
330:                         throw new ClipperImportFormatException("Regex does not match operation signature!");
331:                 }
332:                 
333:         }
334:         
335:         /**
336:          * Extracts first comment before class keyword as class comment and saves it.
337:          *
338:          * @param classRepoDir
339:          * @param file
340:          * @param withoutcommentFile
341:          *
342:          * @return class start
343:          * @throws IOException
344:          * @throws ClipperImportFormatException
345:          */
346:         private int extractClassComment(final File classRepoDir, final String file, final String withoutcommentFile)
347:                         throws IOException, ClipperImportFormatException {
348:                 final String classKeyword = "\\s(class|interface)\\s";
349:                 final Pattern p2 = Pattern.compile(classKeyword, Pattern.DOTALL);
350:                 final Matcher m2 = p2.matcher(withoutcommentFile);
351:                 if (m2.find()) {
352:                         final int start = m2.start();
353:                         final Tuple<Integer, Integer> commentBefore =
354:                                         ClipperUtils.getCommentBefore(start, file, withoutcommentFile);
355:                         final int commentStart = commentBefore.getA();
356:                         final int commentEnd = commentBefore.getB();
357:                         if (!(commentStart == commentEnd)) {
358:                                 FileUtils.saveToFile(file.substring(commentStart, commentEnd), new File(classRepoDir.getAbsolutePath()
359:                                                 + FILESYSTEM_PATH_SEPERATOR + ClipperUtils.CLASSCOMMENT_FILENAME));
360:                         }
361:                         return file.indexOf(String.valueOf(CURLY_BRACKET_OPEN), start);
362:                 }
363:                 throw new ClipperImportFormatException("Keyword 'class' or 'interface' not found!");
364:                 
365:         }
366:         
367:         /**
368:          * Extracts the non-generation part from the file and saves it in the clipper repository.
369:          *
370:          * @param classRepoDir
371:          * @param file
372:          *
373:          * @return the file without the non generation part
374:          * @throws IOException
375:          */
376:         private String extractNonGenerationPart(final File classRepoDir, final String file) throws IOException {
377:                 String fileWithoutNonGenerationPart = file;
378:                 final Pattern p2 = Pattern.compile(ClipperUtils.NON_GEN_PART_MATCH, Pattern.DOTALL);
379:                 final Matcher m2 = p2.matcher(file);
380:                 if (m2.matches()) {
381:                         String nongenpart = file.substring(m2.start(1), m2.end(1));
382:                         nongenpart = nongenpart.replaceFirst("\\r|\\n", "");
383:                         FileUtils.saveToFile(nongenpart, new File(classRepoDir.getAbsolutePath() + FILESYSTEM_PATH_SEPERATOR
384:                                         + ClipperUtils.PROTECTED_AREA_FILENAME));
385:                         fileWithoutNonGenerationPart = file.substring(0, m2.start(1)) + "\r\n" + file.substring(m2.end(1));
386:                 }
387:                 return fileWithoutNonGenerationPart;
388:         }
389:         
390:         /**
391:          * Returns the position of the corresponding closing curly bracket.
392:          *
393:          * @param openBracketPos
394:          * postion of the {-symbol.
395:          * @param file
396:          * file to be searched in
397:          * @return position
398:          * @throws ClipperImportFormatException
399:          * when the bracket cannot be found
400:          */
401:         public static Integer findCorrespondingClosingCurlyBracket(final int openBracketPos, final String file)
402:                         throws ClipperImportFormatException {
403:                 Integer lastOpenedBracketPos = openBracketPos;
404:                 Integer openBracketMinusClosed = 1;
405:                 while (openBracketMinusClosed != 0) {
406:                         final int indexOfClose = file.indexOf(String.valueOf(CURLY_BRACKET_CLOSE), lastOpenedBracketPos + 1);
407:                         final int indexOfOpen = file.indexOf(String.valueOf(CURLY_BRACKET_OPEN), lastOpenedBracketPos + 1);
408:                         if (indexOfClose < indexOfOpen || indexOfOpen == -1) {
409:                                 openBracketMinusClosed--;
410:                                 lastOpenedBracketPos = indexOfClose;
411:                         } else {
412:                                 openBracketMinusClosed++;
413:                                 lastOpenedBracketPos = indexOfOpen;
414:                         }
415:                         if (indexOfClose == -1) {
416:                                 throw new ClipperImportFormatException("Unbalanced Brackets!");
417:                         }
418:                 }
419:                 return lastOpenedBracketPos;
420:         }
421:         
422:         /**
423:          * Replaces illegal chars in String by replacement character (_). Good for filesystem names.
424:          *
425:          * @param name
426:          * name possibly containing illegal characters
427:          * @return normalized string without illegal characters
428:          */
429:         public static String replaceIllegalChars(final String name) {
430:                 return name.replaceAll(ClipperUtils.GENERIC_OPEN, ClipperUtils.GENERIC_OPEN_REPLACEMENT).replaceAll(
431:                                 ClipperUtils.GENERIC_CLOSE,
432:                                 ClipperUtils.GENERIC_CLOSE_REPLACEMENT);
433:         }
434:         
435:         /**
436:          * Creates the directory for the class repo.
437:          *
438:          * @param fullQualifiedName
439:          * with groups qualified name
440:          * @throws TaskException
441:          * when directory cannot be created
442:          * @return created path
443:          */
444:         private File createDirectoryForClass(final String fullQualifiedName) throws TaskException {
445:                 String directoryName = fullQualifiedName;
446:                 if (directoryName.toLowerCase().endsWith(ClipperUtils.FILE_ENDING)) {
447:                         directoryName = directoryName.substring(0, fullQualifiedName.length() - ClipperUtils.FILE_ENDING.length());
448:                 }
449:                 final String path =
450:                                 this.configuration.getRepoDirectoryRoot() + FILESYSTEM_PATH_SEPERATOR
451:                                                 + directoryName.replace(".", FILESYSTEM_PATH_SEPERATOR);
452:                 final File result = new File(path);
453:                 final boolean success = result.mkdirs();
454:                 if (!success) {
455:                         throw new TaskException("Directory creation failed: " + path);
456:                 }
457:                 return result;
458:         }
459:         
460: }