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