haxe/ui/macros/ModuleMacros.hx
package haxe.ui.macros;
import haxe.ui.util.TypeConverter;
#if macro
import haxe.ds.ArraySort;
import haxe.io.Path;
import haxe.macro.Context;
import haxe.macro.Expr.TypePath;
import haxe.macro.Expr;
import haxe.ui.core.ComponentClassMap;
import haxe.ui.macros.ComponentMacros.BuildData;
import haxe.ui.macros.helpers.ClassBuilder;
import haxe.ui.macros.helpers.CodeBuilder;
import haxe.ui.parsers.modules.Module;
import haxe.ui.parsers.modules.ModuleParser;
import haxe.ui.util.StringUtil;
import sys.FileSystem;
import sys.io.File;
#end
@:access(haxe.ui.macros.ComponentMacros)
class ModuleMacros {
#if macro
private static var _modules:Array<Module> = [];
private static var _modulesProcessed:Bool;
private static var _resourceIds:Array<String> = [];
public static var properties:Map<String, String> = new Map<String, String>();
#end
macro public static function processModules():Expr {
if (_modulesProcessed == true) {
return macro null;
}
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.processModules");
#end
/*
_modules = [];
_resourceIds = [];
ComponentClassMap.clear();
*/
loadModules();
var preloadAll:Bool = false;
var builder = new CodeBuilder();
for (m in _modules) {
if (m.preloadList == "all") {
preloadAll = true;
}
if (m.preloader != null) {
var p = m.preloader;
builder.add(macro
haxe.ui.HaxeUIApp.instance.preloaderClass = cast Type.resolveClass($v{p})
);
}
// add resources as haxe resources (plus prefix)
#if resource_resolution_verbose
var moduleId = m.id;
if (moduleId == null) {
moduleId = "unamed";
}
Sys.println("adding resources for module '" + moduleId +"'");
#end
var resourceList = [];
for (r in m.resourceEntries) {
var inclusions = ModuleResourceEntry.globalInclusions.concat(r.inclusions);
var exclusions = ModuleResourceEntry.globalExclusions.concat(r.exclusions);
if (r.path != null) {
if (r.prefix == null) {
r.prefix = r.path;
}
var resolvedPaths = resolvePaths(r.path);
if (resolvedPaths == null || resolvedPaths.length == 0) {
trace("WARNING: Could not resolve resource path " + r.path);
} else {
for (resolvedPath in resolvedPaths) {
addResources(resolvedPath, resolvedPath, r.prefix, inclusions, exclusions, resourceList);
}
}
}
}
#if resource_resolution_verbose
Sys.println("");
#end
// setup themes
for (t in m.themeEntries) {
if (t.parent != null) {
builder.add(macro
haxe.ui.themes.ThemeManager.instance.getTheme($v{t.name}).parent = $v{t.parent}
);
}
for (r in t.styles) {
var useResource = true;
if (r.resource != null && r.resource != "" && resourceList.indexOf(r.resource) == -1) {
useResource = false;
}
if (useResource) {
builder.add(macro
haxe.ui.themes.ThemeManager.instance.addStyleResource($v{t.name}, $v{r.resource}, $v{r.priority}, $v{r.styleData})
);
}
}
for (r in t.images) {
builder.add(macro
haxe.ui.themes.ThemeManager.instance.addImageResource($v{t.name}, $v{r.id}, $v{r.resource}, $v{r.priority})
);
}
for (r in t.vars.keys()) {
var v = t.vars.get(r);
builder.add(macro
haxe.ui.themes.ThemeManager.instance.setThemeVar($v{t.name}, $v{r}, $v{v})
);
}
}
// set toolkit properties
for (p in m.properties) {
builder.add(macro
haxe.ui.Toolkit.properties.set($v{p.name}, $v{p.value})
);
}
for (p in m.preload) {
builder.add(macro
haxe.ui.ToolkitAssets.instance.preloadList.push({type: $v{p.type}, resourceId: $v{p.id}})
);
}
for (c in m.componentEntries) {
if (c.loadAll == true) { // loadAll means will be populate the classmap - this means a ref will be made to EACH of these components
var types = MacroHelpers.typesFromClassOrPackage(c.className, c.classPackage);
if (types != null) {
for (t in types) {
var classInfo = new ClassBuilder(t);
if (classInfo.hasSuperClass("haxe.ui.core.Component")) {
var fullPath = classInfo.fullPath;
builder.add(macro
haxe.ui.core.ComponentClassMap.register($v{classInfo.name}, $v{fullPath})
);
} else if (classInfo.hasInterface("haxe.ui.IComponentDelegate")) {
var fullPath = classInfo.fullPath;
builder.add(macro
haxe.ui.core.ComponentClassMap.register($v{classInfo.name}, $v{fullPath})
);
}
}
}
}
}
processLayoutEntries(m.layoutEntries, builder);
for (l in m.locales) {
var localeId = l.id;
if (localeId == null) {
continue;
}
for (r in l.resources) {
if (r != null) {
builder.add(macro
haxe.ui.locale.LocaleManager.instance.parseResource($v{localeId}, $v{r})
);
}
}
}
for (validator in m.validators) {
var id = validator.id;
var className = validator.className;
var parts = className.split(".");
var name:String = parts.pop();
var t:TypePath = {
pack: parts,
name: name
}
var convertedProperties:Map<String, Any> = null;
if (validator.properties != null) {
for (propertyName in validator.properties.keys()) {
var propertyValue = validator.properties.get(propertyName);
if (convertedProperties == null) {
convertedProperties = new Map<String, Any>();
}
convertedProperties.set(propertyName, TypeConverter.convertFrom(propertyValue));
}
}
builder.add(macro
haxe.ui.validators.ValidatorManager.instance.registerValidator($v{id}, function() {
return new $t();
}, $v{convertedProperties})
);
}
for (inputSource in m.actionInputSources) {
var className = inputSource.className;
var parts = className.split(".");
var name:String = parts.pop();
var t:TypePath = {
pack: parts,
name: name
}
builder.add(macro
haxe.ui.actions.ActionManager.instance.registerInputSource(new $t())
);
}
for (imageLoader in m.imageLoaders) {
var className = imageLoader.className;
var parts = className.split(".");
var name:String = parts.pop();
var t:TypePath = {
pack: parts,
name: name
}
builder.add(macro
haxe.ui.loaders.image.ImageLoader.instance.register($v{imageLoader.prefix}, function() {
return new $t();
}, $v{imageLoader.pattern}, $v{imageLoader.isDefault}, $v{imageLoader.singleInstance})
);
}
for (cssFunction in m.cssFunctions) {
builder.add(macro
haxe.ui.styles.CssFunctions.registerCssFunction($v{cssFunction.name}, $p{cssFunction.call.split(".")})
);
}
for (cssFilter in m.cssFilters) {
var ctor = cssFilter.className + ".new";
builder.add(macro
haxe.ui.styles.CssFilters.registerCssFilter($v{cssFilter.name}, $p{ctor.split(".")})
);
}
for (cssDirective in m.cssDirectives) {
var ctor = cssDirective.className + ".new";
builder.add(macro
haxe.ui.styles.DirectiveHandler.registerDirectiveHandler($v{cssDirective.name}, $p{ctor.split(".")})
);
}
}
if (preloadAll) {
for (r in _resourceIds) {
if (isImage(r)) {
builder.add(macro
haxe.ui.ToolkitAssets.instance.preloadList.push({type: "image", resourceId: $v{r}})
);
} else if (isFont(r)) {
builder.add(macro
haxe.ui.ToolkitAssets.instance.preloadList.push({type: "font", resourceId: $v{r}})
);
}
}
}
populateDynamicClassMap();
for (alias in ComponentClassMap.list()) {
builder.add(macro
haxe.ui.core.ComponentClassMap.register($v{alias}, $v{ComponentClassMap.get(alias)})
);
}
_modulesProcessed = true;
#if haxeui_macro_times
stopTimer();
#end
return builder.expr;
}
#if macro
private static function resolvePaths(path:String):Array<String> {
var paths = [];
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.resolvePaths");
#end
if (Path.isAbsolute(path)) {
var isDir = FileSystem.exists(path) && FileSystem.isDirectory(path);
if (isDir == true) {
paths.push(path);
}
}
for (c in Context.getClassPath()) {
if (c.length == 0) {
c = Sys.getCwd();
}
var p = Path.normalize(c + "/" + path);
var isDir = FileSystem.exists(p) && FileSystem.isDirectory(p);
if (isDir == true) {
paths.push(p);
}
}
#if haxeui_macro_times
stopTimer();
#end
return paths;
}
private static var virtualModuleMap:Map<String, String> = new Map<String, String>();
public static function resolveComponentClass(name:String, namespace:String = null):String {
#if macro_times_verbose
var stopTimer = Context.timer("ModuleMacros.resolveComponentClass");
#end
populateDynamicClassMap();
if (namespace == null) {
namespace = Module.DEFAULT_HAXEUI_NAMESPACE;
}
var qualifiedName = namespace + "/" + name;
name = name.toLowerCase();
#if component_resolution_verbose
Sys.print("resolving component class '" + qualifiedName + "'");
#end
var resolvedClass = ComponentClassMap.get(qualifiedName);
if (resolvedClass != null) {
#if component_resolution_verbose
Sys.println(" => " + resolvedClass + " (from cache)");
#end
#if macro_times_verbose
stopTimer();
#end
return resolvedClass;
}
var modules:Array<Module> = loadModules();
var namespaceToClassPath:Map<String, Array<String>> = new Map<String, Array<String>>();
var namespaceMap:Map<String, String> = new Map<String, String>();
// maybe move this to a new function and only populate once, concern is about language server and caching
for (m in modules) {
for (nsp in m.namespaces.keys()) {
var nsv = m.namespaces.get(nsp);
var list = namespaceToClassPath.get(nsp);
if (list == null) {
list = [];
namespaceToClassPath.set(nsp, list);
}
if (list.indexOf(m.classPath) == -1) {
list.push(m.classPath);
}
namespaceMap.set(nsp, nsv);
}
}
var namespacePrefix = null;
for (mapNamespacePrefix in namespaceMap.keys()) {
var mapNamespaceValue = namespaceMap.get(mapNamespacePrefix);
if (mapNamespaceValue == namespace) {
namespacePrefix = mapNamespacePrefix;
break;
}
}
#if component_resolution_verbose
var pathsSearched:Map<String, String> = new Map<String, String>();
var classPackages:Map<String, String> = new Map<String, String>();
#end
for (m in modules) {
for (c in m.componentEntries) {
var types = null;
if (c.className != null) {
var className = c.className.toLowerCase().split(".").pop();
if (name == className) {
types = Context.getModule(c.className);
}
} else if (c.classPackage != null) {
#if component_resolution_verbose
classPackages.set(c.classPackage, c.classPackage);
#end
var paths = namespaceToClassPath.get(namespacePrefix);
var arr:Array<String> = c.classPackage.split(".");
for (path in paths) {
#if component_resolution_verbose
pathsSearched.set(path, path);
#end
var dir:String = path + arr.join("/");
if (!sys.FileSystem.exists(dir) || !sys.FileSystem.isDirectory(dir)) {
continue;
}
var files:Array<String> = sys.FileSystem.readDirectory(dir);
if (files != null) {
for (file in files) {
if (StringTools.endsWith(file, ".hx") && !StringTools.startsWith(file, ".")) {
var fileName:String = file.substr(0, file.length - 3);
if (fileName.toLowerCase() == name) {
var pkg = c.classPackage + ".";
if (c.classPackage == ".") {
pkg = "";
}
types = Context.getModule(pkg + fileName);
break;
}
}
}
}
if (types != null) {
break;
}
}
}
if (types != null) {
for (t in types) {
resolvedClass = resolveComponentClassInternal(t, c, namespace);
if (resolvedClass != null) {
return resolvedClass;
}
}
}
}
}
if (resolvedClass == null) {
var module = virtualModuleMap.get(name);
if (module != null) {
var m = Context.getModule(module);
for (t in m) {
var resolvedClass = resolveComponentClassInternal(t, null, namespace);
if (resolvedClass != null) {
return resolvedClass;
}
}
}
}
#if component_resolution_verbose
if (resolvedClass == null) {
Sys.println(" => NOT FOUND!");
Sys.println(" Paths searched:");
for (key in pathsSearched.keys()) {
Sys.println(" * " + key);
}
Sys.println(" Registered class packages:");
for (key in classPackages.keys()) {
Sys.println(" * " + key);
}
}
#end
#if macro_times_vebose
stopTimer();
#end
return resolvedClass;
}
public static function defineComponentType(typeDef:TypeDefinition) {
var name = typeDef.name.toLowerCase();
name = StringTools.replace(name, "_", "");
virtualModuleMap.set(name, typeDef.name);
Context.defineModule(typeDef.name, [typeDef]);
}
private static function resolveComponentClassInternal(t:haxe.macro.Type, c:ModuleComponentEntry, namespace:String) {
var orgType = t;
var builder = new ClassBuilder(t);
if (builder.isPrivate == true) {
return null;
}
var org = new ClassBuilder(orgType);
var resolvedClass:String = null;
if (builder.hasSuperClass("haxe.ui.core.Component") == true) {
resolvedClass = builder.fullPath;
if (c != null && c.className != null && org.fullPath != c.className) {
return null;
}
var resolvedClassName = org.name;
if (builder.hasInterface("haxe.ui.core.IDirectionalComponent")) {
if (StringTools.startsWith(resolvedClassName, "Horizontal")) { // alias HorizontalComponent with hcomponent
ComponentClassMap.register(namespace + "/" + "h" + StringTools.replace(resolvedClassName, "Horizontal", "").toLowerCase(), resolvedClass);
} else if (StringTools.startsWith(resolvedClassName, "Vertical")) { // alias VerticalComponent with vcomponent
ComponentClassMap.register(namespace + "/" + "v" + StringTools.replace(resolvedClassName, "Vertical", "").toLowerCase(), resolvedClass);
} else {
var parts = builder.fullPath.split(".");
var tempName = parts.pop();
var hname = "Horizontal" + tempName;
var hclass = parts.join(".") + "." + hname;
ComponentClassMap.register(namespace + "/" + hname.toLowerCase(), hclass);
ComponentClassMap.register(namespace + "/" + ("h" + tempName).toLowerCase(), hclass);
var vname = "Vertical" + tempName;
var vclass = parts.join(".") + "." + vname;
ComponentClassMap.register(namespace + "/" + vname.toLowerCase(), vclass);
ComponentClassMap.register(namespace + "/" + ("v" + tempName).toLowerCase(), vclass);
}
}
ComponentClassMap.register(namespace + "/" + resolvedClassName.toLowerCase(), resolvedClass);
#if component_resolution_verbose
Sys.println(" => " + resolvedClass);
#end
return resolvedClass;
}
return null;
}
private static var _dynamicClassMapPopulated:Bool = false;
private static function populateDynamicClassMap() {
if (_dynamicClassMapPopulated == true) {
return;
}
_dynamicClassMapPopulated = true;
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.populateDynamicClassMap");
#end
var modules:Array<Module> = loadModules();
var list = [];
for (m in modules) {
for (c in m.componentEntries) {
if (c.classFolder != null) {
findDynamicClasses(c.classFolder, list);
} else if (c.classFile != null) {
list.push(c.classFile);
}
}
}
list = orderDynamicClassesByDependency(list);
for (file in list) {
createDynamicClass(file, null, null, null);
}
#if haxeui_macro_times
stopTimer();
#end
}
private static function findDynamicClasses(dir:String, list:Array<String>) {
var resolvedPath = null;
try {
resolvedPath = Context.resolvePath(dir);
} catch (e:Dynamic) {
resolvedPath = haxe.io.Path.join([Sys.getCwd(), dir]);
}
if (resolvedPath == null || FileSystem.exists(resolvedPath) == false || FileSystem.isDirectory(resolvedPath) == false) {
trace("WARNING: Could not find path " + resolvedPath);
}
dir = Path.normalize(resolvedPath);
var contents = FileSystem.readDirectory(dir);
for (item in contents) {
var fullPath = Path.normalize(dir + "/" + item);
if (FileSystem.isDirectory(fullPath)) {
findDynamicClasses(fullPath, list);
} else {
list.push(fullPath);
}
}
}
private static function orderDynamicClassesByDependency(list:Array<String>) {
var classNames = [];
var classNameToFile:Map<String, String> = new Map<String, String>();
for (filename in list) {
var className:String = StringUtil.capitalizeFirstLetter(StringUtil.capitalizeHyphens(new Path(filename).file)).toLowerCase();
classNames.push(className);
classNameToFile.set(className, filename);
}
for (filename in list) {
var resolvedPath = null;
try {
resolvedPath = Context.resolvePath(filename);
} catch (e:Dynamic) {
resolvedPath = haxe.io.Path.join([Sys.getCwd(), filename]);
}
if (resolvedPath == null || FileSystem.exists(resolvedPath) == false || FileSystem.isDirectory(resolvedPath) == true) {
trace("WARNING: Could not find path " + resolvedPath);
}
var fullPath = Path.normalize(resolvedPath);
var className:String = StringUtil.capitalizeFirstLetter(StringUtil.capitalizeHyphens(new Path(filename).file)).toLowerCase();
var fileContents = sys.io.File.getContent(fullPath);
try {
var xml = Xml.parse(fileContents);
walkXmlNodes(className, xml.firstElement(), classNames);
} catch(e:Dynamic) {}
}
var orderedList = [];
for (className in classNames) {
orderedList.push(classNameToFile.get(className));
}
return orderedList;
}
private static function walkXmlNodes(currentClassName:String, xml:Xml, classNames:Array<String>) {
var nodeName = StringTools.replace(xml.nodeName.toLowerCase(), "-", "");
if (classNames.indexOf(nodeName) != -1) {
var currentClassIndex = classNames.indexOf(currentClassName);
var dependencyClassIndex = classNames.indexOf(nodeName);
if (dependencyClassIndex > currentClassIndex) {
classNames.remove(nodeName);
classNames.insert(currentClassIndex, nodeName);
}
}
for (el in xml.elements()) {
walkXmlNodes(currentClassName, el, classNames);
}
}
private static function buildDynamicClassDep(filename:String) {
var className:String = StringUtil.capitalizeFirstLetter(StringUtil.capitalizeHyphens(new Path(filename).file));
trace("build deps for", filename, className);
}
private static function createDynamicClasses(dir:String, root:String, namespaces:Map<String, String>) {
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.createDynamicClasses");
#end
var resolvedPath = null;
try {
resolvedPath = Context.resolvePath(dir);
} catch (e:Dynamic) {
resolvedPath = haxe.io.Path.join([Sys.getCwd(), dir]);
}
if (resolvedPath == null || FileSystem.exists(resolvedPath) == false || FileSystem.isDirectory(resolvedPath) == false) {
trace("WARNING: Could not find path " + resolvedPath);
}
dir = Path.normalize(resolvedPath);
if (root == null) {
root = dir;
}
var contents = FileSystem.readDirectory(dir);
for (item in contents) {
var fullPath = Path.normalize(dir + "/" + item);
if (FileSystem.isDirectory(fullPath)) {
createDynamicClasses(fullPath, root, namespaces);
} else {
createDynamicClass(fullPath, null, root, namespaces);
}
}
#if haxeui_macro_times
stopTimer();
#end
}
public static function createDynamicClass(filePath:String, alias:String = null, root:String = null, namespaces:Map<String, String> = null):String {
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.createDynamicClass");
#end
var resolvedPath = null;
try {
resolvedPath = Context.resolvePath(filePath);
} catch (e:Dynamic) {
resolvedPath = haxe.io.Path.join([Sys.getCwd(), filePath]);
}
if (resolvedPath == null || FileSystem.exists(resolvedPath) == false || FileSystem.isDirectory(resolvedPath) == true) {
trace("WARNING: Could not find path " + resolvedPath);
}
var fullPath = Path.normalize(resolvedPath);
if (root != null) {
filePath = StringTools.replace(fullPath, root, "");
}
var fileParts = filePath.split("/");
var fileName = fileParts.pop();
var className:String = StringUtil.capitalizeFirstLetter(StringUtil.capitalizeHyphens(new Path(fileName).file));
if (alias != null) {
className = StringUtil.capitalizeFirstLetter(alias);
}
var temp = [];
for (part in fileParts) {
part = StringTools.trim(part);
if (part == "" || part == "." || part == "..") {
continue;
}
part = StringTools.replace(part, "-", "");
temp.push(part.toLowerCase());
}
fileParts = temp;
/* this causes problems with language server it seems
var fullClass = fileParts.concat([className]).join(".");
if (ComponentClassMap.hasClass(fullClass) == true) {
return fullClass;
}
*/
var xml = sys.io.File.getContent(fullPath);
var buildData:BuildData = { };
var codeBuilder = new CodeBuilder();
var c = ComponentMacros.buildComponentFromStringCommon(codeBuilder, xml, buildData);
var superClassString = "haxe.ui.containers.Box";
var superClassLookup:String = ModuleMacros.resolveComponentClass(c.type, c.namespace);
if (superClassLookup != null) {
superClassString = superClassLookup;
}
var superClassParts = superClassString.split(".");
var superClass:TypePath = {
name: superClassParts.pop(),
pack: superClassParts
}
var newClass = macro
////////////////////////////////////////////////////////////////////////////////////////////////////////
class $className extends $superClass {
public function new() {
super();
$e{codeBuilder.expr}
}
private override function createChildren() {
super.createChildren();
}
};
var classBuilder = new ClassBuilder(newClass.fields, Context.currentPos());
for (name in buildData.namedComponents.keys()) {
var typeClass = buildData.namedComponents.get(name).type;
var typeParts = typeClass.split(".");
var typeName = typeParts.pop();
var t:TypePath = {
name: typeName,
pack: typeParts
}
classBuilder.addVar(name, ComplexType.TPath(t));
classBuilder.ctor.add(macro $i{name} = findComponent($v{name}, $p{typeClass.split(".")}));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
var fullScript = "";
for (scriptString in c.scriptlets) {
fullScript += scriptString;
}
if (fullScript.length > 0) {
ComponentMacros.buildScriptFunctions(classBuilder, null, buildData.namedComponents, fullScript);
}
Context.defineModule(fileParts.concat([className]).join("."), [newClass]);
if (namespaces != null) {
for (k in namespaces.keys()) {
var ns = namespaces.get(k);
ComponentClassMap.register(ns + "/" + className, fileParts.concat([className]).join("."));
}
} else {
ComponentClassMap.register(Module.DEFAULT_HAXEUI_NAMESPACE + "/" + className, fileParts.concat([className]).join("."));
}
#if haxeui_macro_times
stopTimer();
#end
return fileParts.concat([className]).join(".");
}
private static var _modulesLoaded:Bool = false;
public static function loadModules():Array<Module> {
if (_modulesLoaded == true) {
return _modules;
}
_modulesLoaded = true;
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.loadModules");
#end
#if module_resolution_verbose
Sys.println("scanning class path for modules");
#end
#if haxeui_macro_times
var stopTimerScan = Context.timer("ModuleMacros.loadModules - scanClassPath");
#end
var moduleDetails:Array<{filePath:String, fileContents:String, hash:String, base:String}> = [];
MacroHelpers.scanClassPath(function(filePath:String, base:String) {
#if module_resolution_verbose
Sys.println(" module found at '" + filePath + "' (base: '" + base + "')");
#end
var moduleParser = ModuleParser.get(MacroHelpers.extension(filePath));
if (moduleParser != null) {
var fileContents = File.getContent(filePath);
var hash = haxe.crypto.Md5.encode(fileContents);
var found = false;
for (details in moduleDetails) {
if (details.hash == hash) {
found = true;
break;
}
}
if (!found) {
moduleDetails.push({
filePath: filePath,
fileContents: fileContents,
hash: hash,
base: base
});
}
}
return false;
}, ["module."]);
for (moduleDetail in moduleDetails) {
var moduleParser = ModuleParser.get(MacroHelpers.extension(moduleDetail.filePath));
if (moduleParser != null) {
try {
var module:Module = moduleParser.parse(moduleDetail.fileContents, Context.getDefines(), moduleDetail.filePath);
module.validate();
module.rootPath = new Path(moduleDetail.filePath).dir;
module.classPath = moduleDetail.base;
_modules.push(module);
} catch (e:Dynamic) {
Sys.println('WARNING: Problem parsing module ${MacroHelpers.extension(moduleDetail.filePath)} (${moduleDetail.filePath}) - ${e} (skipping file)');
}
}
}
#if module_resolution_verbose
Sys.println(_modules.length + " module(s) found\n");
#end
#if haxeui_macro_times
stopTimerScan();
#end
ArraySort.sort(_modules, function(a, b):Int {
if (a.priority < b.priority) return -1;
else if (a.priority > b.priority) return 1;
return 0;
});
for (entry in MacroHelpers.classPathCache) {
var entryModule = null;
for (m in _modules) {
if (StringTools.startsWith(entry.path, m.rootPath)) {
entryModule = m;
break;
}
}
if (entryModule != null) {
entry.priority = entryModule.priority;
}
}
ArraySort.sort(MacroHelpers.classPathCache, function(a, b):Int {
if (a.priority < b.priority) return -1;
else if (a.priority > b.priority) return 1;
return 0;
});
for (m in _modules) {
for (p in m.properties) {
properties.set(p.name, p.value);
}
processLayoutEntries(m.layoutEntries);
}
#if haxeui_macro_times
stopTimer();
#end
return _modules;
}
private static function processLayoutEntries(layoutEntries:Array<ModuleLayoutEntry>, builder:CodeBuilder = null) {
for (l in layoutEntries) {
var types = MacroHelpers.typesFromClassOrPackage(l.className, l.classPackage);
if (types != null) {
for (t in types) {
var classInfo = new ClassBuilder(t);
if (classInfo.hasSuperClass("haxe.ui.layouts.Layout")) {
var fullPath = classInfo.fullPath;
for (alias in LayoutMacros.buildLayoutAliases(fullPath)) {
if (builder != null) {
builder.add(macro haxe.ui.layouts.LayoutFactory.register($v{alias}, $v{fullPath}));
}
haxe.ui.layouts.LayoutFactory.register(alias, fullPath);
}
}
}
}
}
}
private static function addResources(path:String, base:String, prefix:String, inclusions:Array<String>, exclusions:Array<String>, resourceList:Array<String>) {
#if haxeui_macro_times
var stopTimer = Context.timer("ModuleMacros.addResources");
#end
if (prefix == null) {
prefix = "";
}
var contents:Array<String> = sys.FileSystem.readDirectory(path);
for (f in contents) {
var file = path + "/" + f;
if (sys.FileSystem.isDirectory(file)) {
addResources(file, base, prefix, inclusions, exclusions, resourceList);
} else {
var relativePath = prefix + StringTools.replace(file, base, "");
var resourceName:String = relativePath;
if (StringTools.startsWith(resourceName, "/")) {
resourceName = resourceName.substr(1, resourceName.length);
}
var includedIndex = isInInclusions(resourceName, inclusions);
var excludedIndex = isInExclusions(resourceName, exclusions);
if (includedIndex != -1 && excludedIndex == -1) {
_resourceIds.push(resourceName);
#if resource_resolution_verbose
if (includedIndex != -1 && includedIndex != 0xffffff) {
var inclusionPattern = inclusions[includedIndex];
if (ModuleResourceEntry.globalInclusions.indexOf(inclusionPattern) != -1) {
Sys.println(" + '" + resourceName + "' - explicitly included via global pattern '" + inclusions[includedIndex] + "'");
} else {
Sys.println(" + '" + resourceName + "' - explicitly included via pattern '" + inclusions[includedIndex] + "'");
}
} else {
Sys.println(" + '" + resourceName);
}
#end
if (resourceList != null) {
resourceList.push(resourceName);
}
Context.addResource(resourceName, File.getBytes(file));
} else {
#if resource_resolution_verbose
if (includedIndex == -1) {
Sys.println(" - '" + resourceName + "' based on inclusion patterns: " + inclusions.join(", "));
}
if (excludedIndex != -1) {
var exclusionPattern = exclusions[excludedIndex];
if (ModuleResourceEntry.globalExclusions.indexOf(exclusionPattern) != -1) {
Sys.println(" - '" + resourceName + "' based on global exclusion pattern '" + exclusions[excludedIndex] + "'");
} else {
Sys.println(" - '" + resourceName + "' based on exclusion pattern '" + exclusions[excludedIndex] + "'");
}
}
#end
}
}
}
#if haxeui_macro_times
stopTimer();
#end
}
private static function isInInclusions(s:String, inclusions:Array<String>):Int {
if (inclusions.length == 0) {
return 0xffffff;
}
var n = -1;
var i = 0;
for (inclusion in inclusions) {
try {
inclusion = fixPattern(inclusion);
var pattern = new EReg(inclusion, "gmi");
if (pattern.match(s)) {
n = i;
break;
}
} catch (e:Dynamic) {
trace("WARNING: inclusion pattern '" + inclusion + "' not valid");
}
i++;
}
return n;
}
private static function isInExclusions(s:String, exclusions:Array<String>):Int {
var n = -1;
var i = 0;
for (exclusion in exclusions) {
try {
exclusion = fixPattern(exclusion);
var pattern = new EReg(exclusion, "gmi");
if (pattern.match(s)) {
n = i;
break;
}
} catch (e:Dynamic) {
trace("WARNING: exclusion pattern '" + exclusion + "' not valid");
}
i++;
}
return n;
}
private static function fixPattern(s:String) { // this just means we can make the regexp a little "simpler"
s = StringTools.replace(s, ".*", "|*");
s = StringTools.replace(s, "/", "\\/");
s = StringTools.replace(s, "\\.", ".");
s = StringTools.replace(s, ".", "\\.");
s = StringTools.replace(s, "|*", ".*");
if (StringTools.startsWith(s, "*")) { // lets "fix" a regexp so you can use things like "*.png" rather than ".*\.png"
s = ".*" + s.substr(1);
}
return s;
}
private static function isImage(file:String):Bool {
return StringTools.endsWith(file, ".png")
|| StringTools.endsWith(file, ".gif")
|| StringTools.endsWith(file, ".svg")
|| StringTools.endsWith(file, ".jpg")
|| StringTools.endsWith(file, ".jpeg");
}
private static function isFont(file:String):Bool {
return StringTools.endsWith(file, ".ttf");
}
#end
}