package com.eas.client.cache;
import com.eas.script.JsDoc;
import com.eas.util.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author mg
*/
public class ApplicationSourceIndexer implements PlatypusIndexer {
public interface ScanCallback {
public void moduleScanned(String aModuleName, ScriptDocument.ModuleDocument aModule, File aFile);
}
protected Path sourcePath;
protected Path apiPath;
protected Map<String, File> id2Paths = new HashMap<>();
protected WatchService service;
protected ScanCallback scanCallback;
protected boolean autoScan = true;
protected ScriptsConfigs scriptsConfigs;
public ApplicationSourceIndexer(Path aSourcePath, Path aApiPath, ScriptsConfigs aScriptsConfigs) throws Exception {
this(aSourcePath, aApiPath, aScriptsConfigs, true, null);
}
public ApplicationSourceIndexer(Path aSourcePath, Path aApiPath, ScriptsConfigs aScriptsConfigs, ScanCallback aScanCallback) throws Exception {
this(aSourcePath, aApiPath, aScriptsConfigs, true, aScanCallback);
}
public ApplicationSourceIndexer(Path aSourcePath, Path aApiPath, ScriptsConfigs aScriptsConfigs, boolean aAutoScan, ScanCallback aScanCallback) throws Exception {
super();
autoScan = aAutoScan;
scriptsConfigs = aScriptsConfigs;
sourcePath = aSourcePath;
apiPath = aApiPath;
scanCallback = aScanCallback;
if (autoScan) {
checkRootDirectory();
scanSource();
}
}
private void checkRootDirectory() throws IllegalArgumentException {
File srcDirectory = sourcePath.toFile();
if (!srcDirectory.exists() || !srcDirectory.isDirectory()) {
throw new IllegalArgumentException(String.format("%s doesn't point to a directory.", sourcePath.toString()));
}
}
public void watch() throws Exception {
service = FileSystems.getDefault().newWatchService();
}
public void unwatch() throws Exception {
assert service != null;
service.close();
service = null;
}
public void rescan() {
id2Paths.clear();
checkRootDirectory();
scanSource();
}
private void scanSource() {
try {
Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path aFilePath, BasicFileAttributes attrs) throws IOException {
if (apiPath != null && aFilePath.startsWith(apiPath)) {
return FileVisitResult.SKIP_SUBTREE;
} else {
try {
add(aFilePath.toFile());
} catch (Exception ex) {
Logger.getLogger(ApplicationSourceIndexer.class.getName()).log(Level.SEVERE, null, ex);
}
return FileVisitResult.CONTINUE;
}
}
});
} catch (Exception ex) {
// Files.walkFileTree may fail due to some programs activity
Logger.getLogger(ApplicationSourceIndexer.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String fileNameWithoutExtension(File aFile) {
String filePath = aFile.getPath();
int dotIndex = filePath.lastIndexOf('.');
if (!filePath.isEmpty() && dotIndex != -1) {
return filePath.substring(0, dotIndex);
} else {
return null;
}
}
protected void add(File aFile) throws Exception {
if (aFile.getName().endsWith(PlatypusFiles.JAVASCRIPT_FILE_END)) {
String defaultModuleName = getDefaultModuleName(aFile);
ScriptDocument scriptDoc = scriptsConfigs.get(defaultModuleName, aFile);
Set<Map.Entry<String, ScriptDocument.ModuleDocument>> modulesDocs = scriptDoc.getModules().entrySet();
modulesDocs.forEach((Map.Entry<String, ScriptDocument.ModuleDocument> aModuleDocEntry) -> {
id2Paths.put(aModuleDocEntry.getKey(), aFile);
if (scanCallback != null) {
scanCallback.moduleScanned(aModuleDocEntry.getKey(), aModuleDocEntry.getValue(), aFile);
}
});
} else if (aFile.getName().endsWith(PlatypusFiles.SQL_FILE_END)) {
String fileContent = FileUtils.readString(aFile, PlatypusFiles.DEFAULT_ENCODING);
String queryName = PlatypusFilesSupport.getAnnotationValue(fileContent, JsDoc.Tag.NAME_TAG);
if (queryName != null && !queryName.isEmpty()) {
id2Paths.put(queryName, aFile);
}
}
}
@Override
public String getDefaultModuleName(File aFile) {
String defaultModuleName = sourcePath.relativize(Paths.get(aFile.toURI())).toString().replace(File.separator, "/");
defaultModuleName = defaultModuleName.substring(0, defaultModuleName.length() - PlatypusFiles.JAVASCRIPT_FILE_END.length());
return defaultModuleName;
}
public Path getAppPath() {
return sourcePath;
}
/**
* Resolves an application element name to a path of local file.
*
* @param aName
* @return
* @throws Exception
*/
@Override
public File nameToFile(String aName) throws Exception {
if (aName != null) {
File file = id2Paths.get(aName);
if (file != null) {
return file;
} else {
String filyName = aName.replace('/', File.separatorChar);
Path appResource = sourcePath.resolve(filyName);
if (appResource.toFile().exists()) {// plain resource relative 'app' directory
return appResource.toFile();
} else {
Path appJsResource = sourcePath.resolve(filyName + PlatypusFiles.JAVASCRIPT_FILE_END);
if (appJsResource.toFile().exists()) {// *.js resource relative 'app' directory
return appJsResource.toFile();
} else {
File absoluteResource = new File(filyName);// plain resource by absolute path
if (absoluteResource.exists()) {
return absoluteResource;
} else {
File absoluteJsResource = new File(filyName + PlatypusFiles.JAVASCRIPT_FILE_END);
if (absoluteJsResource.exists()) {
return absoluteJsResource;
} else {
return null;
}
}
}
}
}
} else {
return null;
}
}
}