Skip to content

Commit

Permalink
added data source support
Browse files Browse the repository at this point in the history
  • Loading branch information
dprzybyl committed Sep 28, 2023
1 parent 96ba004 commit d062258
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 43 deletions.
18 changes: 16 additions & 2 deletions app/aem/core/src/main/antlr/ApmLang.g4
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ path
;

array
: ARRAY_BEGIN arrayValue (',' arrayValue)* ARRAY_END
: ARRAY_BEGIN arrayValue (COMMA arrayValue)* ARRAY_END
;

arrayValue
Expand All @@ -53,7 +53,7 @@ arrayValue
;

structure
: STRUCTURE_BEGIN structureEntry (',' structureEntry)* STRUCTURE_END
: STRUCTURE_BEGIN structureEntry (COMMA structureEntry)* STRUCTURE_END
;

structureEntry
Expand All @@ -65,6 +65,10 @@ structureKey
| STRING_LITERAL
;

dataSource
: identifier BRACKET_BEGIN (argument (COMMA argument)*)? BRACKET_END
;

structureValue
: value
| argument
Expand Down Expand Up @@ -108,6 +112,7 @@ expression
argument
: expression
| path
| dataSource
;

command
Expand Down Expand Up @@ -176,6 +181,15 @@ STRUCTURE_BEGIN
STRUCTURE_END
: '}'
;
BRACKET_BEGIN
: '('
;
BRACKET_END
: ')'
;
COMMA
: ','
;
BLOCK_BEGIN
: 'begin'
| 'BEGIN'
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ApmType> parameters) {
List<Config> configs = determineConfigs(parameters);
Resource root = resolver.getResource("/content");
return traverseTree(root, 0, configs);
}

private ApmType traverseTree(Resource root, int depth, List<Config> configs) {
Config config = configs.get(depth);
List<ApmType> list = new ArrayList<>();
for (Resource resource : root.getChildren()) {
Matcher matcher = config.pattern.matcher(resource.getPath());
if (matcher.matches()) {
Map<String, ApmType> 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<Config> determineConfigs(List<ApmType> parameters) {
String regex = parameters.get(0).getString();
List<ApmType> paramNames = parameters.get(1).getList();
String[] parts = StringUtils.substringAfter(regex, "/content/").split("/");
int paramIndex = 0;
List<Config> 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<String> paramNames;
}
}
Original file line number Diff line number Diff line change
@@ -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<ApmType> parameters);
}
Original file line number Diff line number Diff line change
@@ -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<String, DataSource> 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<ApmType> parameters) {
DataSource dataSource = dataSources.get(name.toLowerCase());
return dataSource == null ? null : dataSource.determine(resolver, parameters);
}
}
Original file line number Diff line number Diff line change
@@ -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<ApmType> 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<ApmString> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -88,6 +89,9 @@ public class ScriptManagerImpl implements ScriptManager {
@Reference
private History history;

@Reference
private DataSourceInvoker dataSourceInvoker;

@Reference(
cardinality = ReferenceCardinality.MULTIPLE,
policy = ReferencePolicy.DYNAMIC,
Expand Down Expand Up @@ -132,7 +136,7 @@ private Progress execute(Script script, ExecutionMode mode, Map<String, String>
progress.addEntry(Status.ERROR, e.getMessage(), commandName);
return Status.ERROR;
}
});
}, dataSourceInvoker);

try {
Map<String, String> definitions = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Script> {
val result = mutableSetOf<Script>()
Expand Down Expand Up @@ -64,7 +66,7 @@ class ReferenceFinder(
private fun fillReferenceGraph(refGraph: ReferenceGraph, script: Script) {
if (refGraph.getNode(script) == null) {
val parsedScript = ParsedScript.create(script).apm
val executionContext = ExecutionContext.create(scriptFinder, resourceResolver, script, ProgressImpl(resourceResolver.userID))
val executionContext = ExecutionContext.create(scriptFinder, resourceResolver, dataSourceInvoker, script, ProgressImpl(resourceResolver.userID))
findReferences(refGraph, refGraph.addNode(script), listOf(script), executionContext, parsedScript)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.cognifide.apm.core.grammar.argument.ArgumentResolverException
import com.cognifide.apm.core.grammar.argument.Arguments
import com.cognifide.apm.core.grammar.common.getIdentifier
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.InvalidSyntaxException
import com.cognifide.apm.core.grammar.parsedscript.InvalidSyntaxMessageFactory
Expand All @@ -43,13 +44,14 @@ class ScriptRunner(
private val scriptFinder: ScriptFinder,
private val resourceResolver: ResourceResolver,
private val validateOnly: Boolean = false,
private val actionInvoker: ActionInvoker
private val actionInvoker: ActionInvoker,
private val dataSourceInvoker: DataSourceInvoker
) {

@JvmOverloads
fun execute(script: Script, progress: Progress, initialDefinitions: Map<String, String> = mapOf()): Progress {
try {
val executionContext = ExecutionContext.create(scriptFinder, resourceResolver, script, progress)
val executionContext = ExecutionContext.create(scriptFinder, resourceResolver, dataSourceInvoker, script, progress)
initialDefinitions.forEach { (name, value) -> executionContext.setVariable(name, ApmString(value)) }
val executor = Executor(executionContext)
executor.visit(executionContext.root.apm)
Expand Down Expand Up @@ -219,7 +221,7 @@ class ScriptRunner(
}

override fun visitImportScript(ctx: ImportScriptContext): Status {
val result = ImportScript(executionContext).import(ctx)
val result = ImportScript(executionContext, resourceResolver, dataSourceInvoker).import(ctx)
executionContext.variableHolder.setAll(result.variableHolder)
progress(ctx, Status.SUCCESS, "import", result.toMessages())
return Status.SUCCESS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ import com.cognifide.apm.core.grammar.antlr.ApmLangParser.*
import com.cognifide.apm.core.grammar.common.getIdentifier
import com.cognifide.apm.core.grammar.common.getKey
import com.cognifide.apm.core.grammar.common.getPath
import com.cognifide.apm.core.grammar.datasource.DataSourceInvoker
import com.cognifide.apm.core.grammar.executioncontext.VariableHolder
import org.apache.commons.lang.text.StrSubstitutor
import org.apache.commons.lang3.StringUtils
import org.apache.sling.api.resource.ResourceResolver

class ArgumentResolver(private val variableHolder: VariableHolder) {
class ArgumentResolver(private val variableHolder: VariableHolder,
private val resolver: ResourceResolver,
private val dataSourceInvoker: DataSourceInvoker) {

private val singleArgumentResolver: SingleArgumentResolver

Expand Down Expand Up @@ -176,5 +180,15 @@ class ArgumentResolver(private val variableHolder: VariableHolder) {
val name = getIdentifier(ctx.variableIdentifier())
return variableHolder[name]
}

override fun visitDataSource(ctx: DataSourceContext): ApmType {
val name = getIdentifier(ctx.identifier())
val values = ctx.children
?.map { child -> child.accept(this) }
?.filter { it !is ApmEmpty }
?: listOf()
return dataSourceInvoker?.determine(name, resolver, values)
?: throw ArgumentResolverException("Data source \"$name\" not found")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,19 @@ import com.cognifide.apm.core.grammar.antlr.ApmLangParser.*
import com.cognifide.apm.core.grammar.argument.ArgumentResolver
import com.cognifide.apm.core.grammar.argument.Arguments
import com.cognifide.apm.core.grammar.common.StackWithRoot
import com.cognifide.apm.core.grammar.datasource.DataSourceInvoker
import com.cognifide.apm.core.grammar.parsedscript.ParsedScript
import com.cognifide.apm.core.logger.Progress
import org.apache.commons.lang3.StringUtils
import org.apache.jackrabbit.api.security.user.Authorizable
import org.apache.sling.api.resource.ResourceResolver

class ExecutionContext private constructor(
private val scriptFinder: ScriptFinder,
private val resourceResolver: ResourceResolver,
val root: ParsedScript,
override val progress: Progress) : ExternalExecutionContext {
private val scriptFinder: ScriptFinder,
private val resourceResolver: ResourceResolver,
private val dataSourceInvoker: DataSourceInvoker,
val root: ParsedScript,
override val progress: Progress) : ExternalExecutionContext {

private val parsedScripts: MutableMap<String, ParsedScript> = mutableMapOf()
private var runScripts: StackWithRoot<RunScript> = StackWithRoot(RunScript(root))
Expand All @@ -48,16 +50,16 @@ class ExecutionContext private constructor(
val variableHolder: VariableHolder
get() = currentRunScript.variableHolder
val argumentResolver: ArgumentResolver
get() = ArgumentResolver(variableHolder)
get() = ArgumentResolver(variableHolder, resourceResolver, dataSourceInvoker)

init {
registerScript(root)
}

companion object {
@JvmStatic
fun create(scriptFinder: ScriptFinder, resourceResolver: ResourceResolver, script: Script, progress: Progress): ExecutionContext {
return ExecutionContext(scriptFinder, resourceResolver, ParsedScript.create(script), progress)
fun create(scriptFinder: ScriptFinder, resourceResolver: ResourceResolver, dataSourceInvoker: DataSourceInvoker, script: Script, progress: Progress): ExecutionContext {
return ExecutionContext(scriptFinder, resourceResolver, dataSourceInvoker, ParsedScript.create(script), progress)
}
}

Expand Down Expand Up @@ -116,7 +118,7 @@ class ExecutionContext private constructor(

private fun fetchScript(path: String): ParsedScript {
val script = scriptFinder.find(path, resourceResolver)
?: throw ScriptExecutionException("Script not found $path")
?: throw ScriptExecutionException("Script not found $path")
val parsedScript = ParsedScript.create(script)
registerScript(parsedScript)
return parsedScript
Expand Down
Loading

0 comments on commit d062258

Please sign in to comment.