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