001package com.ganteater.ae;
002
003import java.io.File;
004import java.io.IOException;
005import java.io.InputStream;
006import java.net.MalformedURLException;
007import java.net.URL;
008import java.security.CodeSource;
009import java.util.ArrayList;
010import java.util.List;
011import java.util.Properties;
012import java.util.Set;
013import java.util.zip.ZipFile;
014
015import org.apache.commons.io.FileUtils;
016import org.apache.commons.io.FilenameUtils;
017import org.apache.commons.io.IOUtils;
018import org.apache.commons.lang.StringUtils;
019
020import com.ganteater.ae.util.xml.easyparser.EasyParser;
021import com.ganteater.ae.util.xml.easyparser.Node;
022
023public class RecipesScanner {
024
025        private AEWorkspace workspace;
026
027        private File startDir;
028
029        private List<String> fPriorityFolders = new ArrayList<String>();
030
031        public RecipesScanner(AEWorkspace workspace, File startDir) {
032                super();
033                this.startDir = startDir;
034                this.workspace = workspace;
035        }
036
037        public Properties scanRecipeSource(String path, Set<Object> skipNames) throws IOException {
038
039                CodeSource codeSource = getClassForRecipeJarLoad().getProtectionDomain().getCodeSource();
040
041                URL location = codeSource.getLocation();
042                String jarFile = location.toString();
043                Properties aTestsDescList = new Properties();
044                String extension = FilenameUtils.getExtension(jarFile);
045                if ("jar".equalsIgnoreCase(extension) || "zip".equalsIgnoreCase(extension)) {
046                        if (StringUtils.startsWith(jarFile, "file:/")) {
047                                jarFile = StringUtils.substringAfter(jarFile, "file:/").replace("%20", " ");
048                        }
049
050                        File file = new File(jarFile);
051                        scanTasksJar(file, path, aTestsDescList, skipNames);
052                }
053
054                return aTestsDescList;
055        }
056
057        private Class<?> getClassForRecipeJarLoad() {
058                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
059                Class<?> clazz = getClass();
060                Class<?> clazzBefore = null;
061                for (StackTraceElement stackTraceElement : stackTrace) {
062                        String className = stackTraceElement.getClassName();
063                        try {
064                                if (StringUtils.startsWithAny(className, new String[] { "org.apache.maven" })) {
065                                        clazz = clazzBefore;
066                                        break;
067                                }
068                                clazzBefore = Class.forName(className);
069
070                        } catch (ClassNotFoundException e) {
071                        }
072                }
073                return clazz;
074        }
075
076        private void scanRecipeDir(String path, Properties aTestsDescList, Set<Object> skipNames) throws IOException {
077                try {
078                        URL source = new URL(path);
079                        String urlPath = StringUtils.substringBeforeLast(source.getPath(), "/");
080                        if (urlPath.startsWith("/")) {
081                                urlPath = urlPath.substring(1);
082                        }
083                        String dirPath = "/recipes/" + source.getHost() + "/" + urlPath.replace("/", "-");
084                        File destination = new File(workspace.getHomeWorkingDir(), dirPath);
085                        String urlFile = StringUtils.substringAfterLast(source.getFile(), "/");
086                        destination = new File(destination, urlFile);
087                        if (!destination.exists()) {
088                                URL url = new URL(path.replace(" ", "%20"));
089                                FileUtils.copyURLToFile(url, destination);
090                        }
091
092                        String absolutePath = destination.getAbsolutePath();
093                        scanRecipeDir(absolutePath, aTestsDescList, skipNames);
094
095                } catch (MalformedURLException e) {
096                        String extension = StringUtils.substringAfterLast(path, ".");
097                        File file = workspace.getFile(path);
098
099                        if (file.isDirectory()) {
100                                scanTasksDir(file, aTestsDescList, skipNames);
101                        } else {
102                                if ("jar".equalsIgnoreCase(extension) || "zip".equalsIgnoreCase(extension)
103                                                || StringUtils.contains(path, ".zip")) {
104                                        scanTasksJar(file, "", aTestsDescList, skipNames);
105                                } else if ("recipe".equalsIgnoreCase(extension)) {
106                                        regTestFile(file.getAbsolutePath(), aTestsDescList, skipNames);
107                                }
108                        }
109                }
110        }
111
112        private void scanTasksJar(final File jar, final String dir, final Properties recipes, final Set<Object> skipNames)
113                        throws IOException {
114
115                try (ZipFile jarFile = new ZipFile(jar)) {
116                        jarFile.stream().forEach(entry -> {
117                                String name = entry.getName();
118                                if (name.startsWith(dir) && !entry.isDirectory()) {
119                                        try (InputStream inputStream = jarFile.getInputStream(entry)) {
120                                                URL url = new URL("jar:file:" + jar.getAbsolutePath() + "!/" + name);
121                                                regTestURL(url.toString(), inputStream, recipes, skipNames);
122                                        } catch (IOException e) {
123                                                e.printStackTrace();
124                                        }
125                                }
126                        });
127                }
128
129        }
130
131        private void regTestURL(String name, InputStream entityStream, Properties aTestsDescList, Set<Object> skipNames) {
132                if (name.toLowerCase().endsWith(".recipe")) {
133
134                        Node theNode = null;
135                        try {
136                                theNode = new EasyParser().getObject(entityStream);
137
138                                if (!"Recipe".equals(theNode.getTag())) {
139                                        return;
140                                }
141
142                                String recipeName = theNode.getAttribute("name");
143
144                                if (!skipNames.contains(recipeName)) {
145                                        String theCurrentFile = aTestsDescList.getProperty(recipeName);
146                                        if (theCurrentFile != null && theCurrentFile.equals(name) == false) {
147                                                theCurrentFile = taskFileNameConflict(recipeName, theCurrentFile, name);
148                                        } else {
149                                                theCurrentFile = name;
150                                        }
151                                        aTestsDescList.setProperty(recipeName, theCurrentFile);
152                                }
153
154                        } catch (Exception e) {
155                                e.printStackTrace();
156                        }
157                }
158        }
159
160        private Properties scanTasksDir(File aDir, Properties aTestsDescList, Set<Object> skipNames) throws IOException {
161                File[] theFiles = aDir.listFiles();
162
163                if (theFiles == null) {
164                        return aTestsDescList;
165                }
166
167                for (int i = 0; i < theFiles.length; i++) {
168
169                        File file = theFiles[i];
170
171                        if (file.isDirectory()) {
172                                scanRecipeDir(file.getAbsolutePath(), aTestsDescList, skipNames);
173
174                        } else {
175                                regTestFile(file.getAbsolutePath(), aTestsDescList, skipNames);
176                        }
177                }
178
179                return aTestsDescList;
180        }
181
182        private void regTestFile(String file, Properties aTestsDescList, Set<Object> skipNames) {
183                file = file.replace('\\', '/');
184                String name = StringUtils.substringAfterLast(file, "/");
185                workspace.progressText(name);
186
187                if (name.toLowerCase().endsWith(".recipe")) {
188
189                        Node theNode = null;
190                        try {
191
192                                File theXMLFile = new File(file);
193                                if (theXMLFile.exists()) {
194                                        theNode = new EasyParser().getObject(theXMLFile);
195                                } else {
196                                        String resourceName = "/" + file;
197                                        try (InputStream resourceAsStream = RecipesScanner.class.getResourceAsStream(resourceName)) {
198                                                String recipeXml = IOUtils.toString(resourceAsStream);
199
200                                                theNode = new EasyParser().getObject(recipeXml);
201                                                file = RecipesScanner.class.getResource(resourceName).toString();
202                                        }
203                                }
204
205                        } catch (Exception e) {
206                                System.out.println("Incorrect file: " + file + ", Error: " + e.getMessage());
207                                theNode = null;
208                        }
209
210                        if (theNode == null || "Recipe".equals(theNode.getTag()) == false) {
211                                return;
212                        }
213
214                        String recipeName = theNode.getAttribute("name");
215                        if (!skipNames.contains(recipeName)) {
216                                String theCurrentFile = aTestsDescList.getProperty(recipeName);
217                                String predefinedFile = workspace.getTestsDescList().getProperty(recipeName);
218                                if (predefinedFile == null) {
219                                        if (theCurrentFile != null && theCurrentFile.equals(file) == false) {
220                                                String conflictFile = file;
221                                                theCurrentFile = taskFileNameConflict(recipeName, theCurrentFile, conflictFile);
222                                        } else {
223                                                theCurrentFile = file;
224                                        }
225                                } else {
226                                        theCurrentFile = predefinedFile;
227                                }
228                                aTestsDescList.setProperty(recipeName, theCurrentFile);
229                        }
230                }
231        }
232
233        public String taskFileNameConflict(String aKey, String aCurrentFile, String aNewFile) {
234                StringBuilder theStringBuffer = new StringBuilder();
235
236                theStringBuffer.append("Duplication file for task with name: '" + aKey + "'\n");
237                theStringBuffer.append("Registration file: '" + aCurrentFile + "'\n");
238                theStringBuffer.append("Conflict file: '" + aNewFile + "'\n");
239                theStringBuffer.append("\n");
240                theStringBuffer.append("Please select priority folder:\n");
241
242                String theFirstFile = new File(aNewFile).getParent();
243                String theSecondFile = new File(aCurrentFile).getParent();
244
245                StringBuffer theMessageBuffer = new StringBuffer();
246                theMessageBuffer.append("Duplication file for task with name: '" + aKey + "' ");
247
248                String thePriorityFolder = null;
249                if (fPriorityFolders.contains(theFirstFile)) {
250                        thePriorityFolder = theFirstFile;
251                        theMessageBuffer.append("presetting selection file: " + aNewFile);
252                } else if (fPriorityFolders.contains(theSecondFile)) {
253                        thePriorityFolder = theSecondFile;
254                        theMessageBuffer.append("presetting selection file: " + aCurrentFile);
255                } else {
256                        String message = theStringBuffer.toString();
257                        String[] possibleValues = new String[] { aCurrentFile, aNewFile };
258
259                        thePriorityFolder = workspace.choicePriorityRecipeFolder(message, possibleValues);
260
261                        theMessageBuffer.append("manual selection priority folder: " + thePriorityFolder);
262                }
263
264                fPriorityFolders.add(new File(thePriorityFolder).getParent());
265
266                if (thePriorityFolder == null) {
267                        StringBuffer fBuffer = new StringBuffer();
268                        fBuffer.append("Duplication file for task with name: '" + aKey + "': ");
269                        fBuffer.append("Registration file: '" + aCurrentFile + "', ");
270                        fBuffer.append("Conflict file: '" + aNewFile + "'");
271                        // log.warn(fBuffer.toString());
272                        throw new ConfigurationException(fBuffer.toString());
273                }
274
275                if (theSecondFile != null && theSecondFile.equals(thePriorityFolder))
276                        aNewFile = aCurrentFile;
277
278                return aNewFile;
279        }
280
281        public File getStartDir() {
282                return startDir;
283        }
284
285        public List<String> getPriorityFolders() {
286                return fPriorityFolders;
287        }
288
289        public void setStartDir(File startDir) {
290                this.startDir = startDir;
291        }
292
293        public void loadTaskPath(Node configNode, TemplateProcessor tp) throws IOException {
294                Node[] recipesNodes = configNode.getNodes("Recipes");
295                if (recipesNodes.length == 0) {
296                        recipesNodes = new Node[] { new EasyParser().getObject("<Recipes path=\"recipes\" />") };
297                }
298
299                Properties testsDescList = workspace.getTestsDescList();
300                for (int i = recipesNodes.length - 1; i >= 0; i--) {
301                        Node node = recipesNodes[i];
302                        String path = node.getAttribute("path");
303                        path = tp.replaceProperties(path);
304                        Properties aTestsDescList = new Properties();
305                        Set<Object> keySet = testsDescList.keySet();
306                        scanRecipeDir(path, aTestsDescList, keySet);
307                        testsDescList.putAll(aTestsDescList);
308
309                        Properties scanRecipeSource = scanRecipeSource(path, testsDescList.keySet());
310                        testsDescList.putAll(scanRecipeSource);
311                }
312        }
313
314}