diff --git a/app/aem/core/src/main/antlr/ApmLang.g4 b/app/aem/core/src/main/antlr/ApmLang.g4 index 9b7c6df5d..3305453cd 100644 --- a/app/aem/core/src/main/antlr/ApmLang.g4 +++ b/app/aem/core/src/main/antlr/ApmLang.g4 @@ -42,7 +42,7 @@ path ; array - : ARRAY_BEGIN arrayValue (',' arrayValue)* ARRAY_END + : ARRAY_BEGIN arrayValue (COMMA arrayValue)* ARRAY_END ; arrayValue @@ -53,7 +53,7 @@ arrayValue ; structure - : STRUCTURE_BEGIN structureEntry (',' structureEntry)* STRUCTURE_END + : STRUCTURE_BEGIN structureEntry (COMMA structureEntry)* STRUCTURE_END ; structureEntry @@ -65,6 +65,10 @@ structureKey | STRING_LITERAL ; +dataSource + : identifier BRACKET_BEGIN (argument (COMMA argument)*)? BRACKET_END + ; + structureValue : value | argument @@ -108,6 +112,7 @@ expression argument : expression | path + | dataSource ; command @@ -176,6 +181,15 @@ STRUCTURE_BEGIN STRUCTURE_END : '}' ; +BRACKET_BEGIN + : '(' + ; +BRACKET_END + : ')' + ; +COMMA + : ',' + ; BLOCK_BEGIN : 'begin' | 'BEGIN' diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/ContentDataSource.java b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/ContentDataSource.java new file mode 100644 index 000000000..7d205d2b9 --- /dev/null +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/ContentDataSource.java @@ -0,0 +1,79 @@ +package com.cognifide.apm.core.grammar.datasource; + +import com.cognifide.apm.core.grammar.ApmList; +import com.cognifide.apm.core.grammar.ApmMap; +import com.cognifide.apm.core.grammar.ApmString; +import com.cognifide.apm.core.grammar.ApmType; +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Component +public class ContentDataSource implements DataSource { + + @Override + public String getName() { + return "CONTENT"; + } + + @Override + public ApmType determine(ResourceResolver resolver, List parameters) { + List configs = determineConfigs(parameters); + Resource root = resolver.getResource("/content"); + return traverseTree(root, 0, configs); + } + + private ApmType traverseTree(Resource root, int depth, List configs) { + Config config = configs.get(depth); + List list = new ArrayList<>(); + for (Resource resource : root.getChildren()) { + Matcher matcher = config.pattern.matcher(resource.getPath()); + if (matcher.matches()) { + Map map = new HashMap<>(); + map.put("path", new ApmString(resource.getPath())); + map.put("name", new ApmString(resource.getName())); + for (int i = 0; i < config.paramNames.size(); i++) { + map.put(config.paramNames.get(i), new ApmString(matcher.group(i + 1).toLowerCase())); + } + if (depth < configs.size() - 1) { + map.put("pages", traverseTree(resource, depth + 1, configs)); + } + list.add(new ApmMap(map)); + } + } + return new ApmList(list); + } + + private List determineConfigs(List parameters) { + String regex = parameters.get(0).getString(); + List paramNames = parameters.get(1).getList(); + String[] parts = StringUtils.substringAfter(regex, "/content/").split("/"); + int paramIndex = 0; + List configs = new ArrayList<>(); + for (String part : parts) { + Config config = new Config(); + config.pattern = Pattern.compile(".*/" + part); + config.paramNames = new ArrayList<>(); + for (int i = 0; i < StringUtils.countMatches(part, "("); i++) { + config.paramNames.add(paramNames.get(paramIndex++).getString()); + } + configs.add(config); + } + return configs; + } + + private class Config { + + Pattern pattern; + + List paramNames; + } +} diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/DataSource.java b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/DataSource.java new file mode 100644 index 000000000..e3e7f78ca --- /dev/null +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/DataSource.java @@ -0,0 +1,14 @@ +package com.cognifide.apm.core.grammar.datasource; + +import com.cognifide.apm.core.grammar.ApmEmpty; +import com.cognifide.apm.core.grammar.ApmType; +import org.apache.sling.api.resource.ResourceResolver; + +import java.util.List; + +public interface DataSource { + + String getName(); + + ApmType determine(ResourceResolver resolver, List parameters); +} diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/DataSourceInvoker.java b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/DataSourceInvoker.java new file mode 100644 index 000000000..ed4d6f94d --- /dev/null +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/DataSourceInvoker.java @@ -0,0 +1,32 @@ +package com.cognifide.apm.core.grammar.datasource; + +import com.cognifide.apm.core.grammar.ApmType; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component(service = DataSourceInvoker.class) +public class DataSourceInvoker { + + private final Map dataSources = new HashMap<>(); + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE, service = DataSource.class) + protected final void bindDataSource(DataSource dataSource) { + dataSources.put(dataSource.getName().toLowerCase(), dataSource); + } + + protected final void unbindDataSource(DataSource dataSource) { + dataSources.remove(dataSource.getName().toLowerCase()); + } + + public ApmType determine(String name, ResourceResolver resolver, List parameters) { + DataSource dataSource = dataSources.get(name.toLowerCase()); + return dataSource == null ? null : dataSource.determine(resolver, parameters); + } +} diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/NodeNamesDataSource.java b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/NodeNamesDataSource.java new file mode 100644 index 000000000..ed7338e1e --- /dev/null +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/grammar/datasource/NodeNamesDataSource.java @@ -0,0 +1,36 @@ +package com.cognifide.apm.core.grammar.datasource; + +import com.cognifide.apm.core.grammar.ApmList; +import com.cognifide.apm.core.grammar.ApmString; +import com.cognifide.apm.core.grammar.ApmType; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Component; + +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +@Component +public class NodeNamesDataSource implements DataSource { + + @Override + public String getName() { + return "NODE_NAMES"; + } + + @Override + public ApmType determine(ResourceResolver resolver, List parameters) { + String path = parameters.get(0).getString(); + String regex = parameters.size() >= 2 ? parameters.get(1).getString() : "[^:]+"; + Pattern pattern = Pattern.compile(regex); + Resource resource = resolver.getResource(path); + List values = StreamSupport.stream(resource.getChildren().spliterator(), false) + .map(Resource::getName) + .filter(name -> pattern.matcher(name).matches()) + .map(ApmString::new) + .collect(Collectors.toList()); + return new ApmList(values); + } +} diff --git a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java index e369e521d..25cde4f36 100644 --- a/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java +++ b/app/aem/core/src/main/java/com/cognifide/apm/core/scripts/ScriptManagerImpl.java @@ -38,6 +38,7 @@ import com.cognifide.apm.core.actions.executor.ActionExecutorFactory; import com.cognifide.apm.core.executors.ContextImpl; import com.cognifide.apm.core.grammar.ScriptRunner; +import com.cognifide.apm.core.grammar.datasource.DataSourceInvoker; import com.cognifide.apm.core.history.History; import com.cognifide.apm.core.logger.Progress; import com.cognifide.apm.core.progress.ProgressImpl; @@ -88,6 +89,9 @@ public class ScriptManagerImpl implements ScriptManager { @Reference private History history; + @Reference + private DataSourceInvoker dataSourceInvoker; + @Reference( cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, @@ -132,7 +136,7 @@ private Progress execute(Script script, ExecutionMode mode, Map progress.addEntry(Status.ERROR, e.getMessage(), commandName); return Status.ERROR; } - }); + }, dataSourceInvoker); try { Map definitions = new HashMap<>(); diff --git a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ReferenceFinder.kt b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ReferenceFinder.kt index 119719e3b..563baf2e0 100644 --- a/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ReferenceFinder.kt +++ b/app/aem/core/src/main/kotlin/com/cognifide/apm/core/grammar/ReferenceFinder.kt @@ -25,6 +25,7 @@ import com.cognifide.apm.api.scripts.LaunchMode import com.cognifide.apm.api.scripts.Script import com.cognifide.apm.api.services.ScriptFinder import com.cognifide.apm.core.grammar.common.getPath +import com.cognifide.apm.core.grammar.datasource.DataSourceInvoker import com.cognifide.apm.core.grammar.executioncontext.ExecutionContext import com.cognifide.apm.core.grammar.parsedscript.ParsedScript import com.cognifide.apm.core.progress.ProgressImpl @@ -33,7 +34,8 @@ import java.util.* class ReferenceFinder( private val scriptFinder: ScriptFinder, - private val resourceResolver: ResourceResolver) { + private val resourceResolver: ResourceResolver, + private val dataSourceInvoker: DataSourceInvoker) { fun findReferences(script: Script): List