Skip to content

Commit

Permalink
Afs virtual script (powsybl#973)
Browse files Browse the repository at this point in the history
* Add include scripts in AbstractScript nodes

Signed-off-by: Paul Bui-Quang <[email protected]>

* Add generic script type

Signed-off-by: Paul Bui-Quang <[email protected]>
  • Loading branch information
pl-buiquang authored and geofjamg committed Dec 11, 2019
1 parent d55ddf1 commit caed62f
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.powsybl.action.dsl.ActionDb;
import com.powsybl.action.dsl.ActionDslLoader;
import com.powsybl.afs.ProjectFileCreationContext;
import com.powsybl.afs.ext.base.AbstractModificationScript;
import com.powsybl.afs.ext.base.AbstractScript;
import com.powsybl.afs.ext.base.ScriptType;
import com.powsybl.contingency.ContingenciesProvider;
import com.powsybl.contingency.Contingency;
Expand All @@ -23,7 +23,7 @@
/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
*/
public class ActionScript extends AbstractModificationScript implements ContingenciesProvider {
public class ActionScript extends AbstractScript<ActionScript> implements ContingenciesProvider {

public static final String PSEUDO_CLASS = "actionScript";
public static final int VERSION = 0;
Expand All @@ -48,7 +48,7 @@ public ScriptType getScriptType() {

public ActionDb load(Network network) {
Objects.requireNonNull(network);
return new ActionDslLoader(readScript()).load(network);
return new ActionDslLoader(readScript(true)).load(network);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
*/
public class DependencyCache<T> {
public class DependencyCache<T extends ProjectNode> {

private final ProjectFile projectFile;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2019, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.afs;

import org.apache.commons.lang3.tuple.ImmutablePair;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @author Paul Bui-Quang <paul.buiquang at rte-france.com>
*/
public class OrderedDependencyManager {

private final ProjectFile projectFile;

private List<ProjectDependency<ProjectNode>> dependencyCache = null;

public OrderedDependencyManager(ProjectFile projectFile) {
this.projectFile = Objects.requireNonNull(projectFile);
projectFile.addListener(new DefaultProjectFileListener() {
@Override
public void dependencyChanged(String name) {
dependencyCache = null;
}
});
}

public void appendDependencies(String name, List<ProjectNode> projectNodes) {
List<ProjectNode> nodes = getDependencies(name);
nodes.addAll(projectNodes);
setDependencies(name, nodes);
}

public void insertDependencies(String name, int index, List<ProjectNode> projectNodes) {
List<ProjectNode> nodes = getDependencies(name);
nodes.addAll(index, projectNodes);
setDependencies(name, nodes);
}

public void removeDependency(String name, int index) {
List<ProjectNode> nodes = getDependencies(name);
nodes.remove(index);
setDependencies(name, nodes);
}

public void removeDependencies(String name, List<String> nodeIds) {
List<ProjectNode> nodes = getDependencies(name)
.stream()
.filter(dep -> !nodeIds.contains(dep.getId()))
.collect(Collectors.toList());
setDependencies(name, nodes);
}

public void setDependencies(String name, List<ProjectNode> projectNodes) {
getDependenciesFor(name).map(el -> el.getRight().getName()).forEach(projectFile::removeDependencies);
for (int i = 0; i < projectNodes.size(); i++) {
projectFile.setDependencies(name + "_" + i, Collections.singletonList(projectNodes.get(i)));
}
}

public List<ProjectNode> getDependencies(String name) {
return getDependenciesFor(name)
.sorted(Comparator.comparing(ImmutablePair::getLeft))
.map(dep -> dep.getRight().getProjectNode())
.collect(Collectors.toList());
}

public <T extends ProjectNode> List<T> getDependencies(String name, Class<T> nodeClass) {
return getDependenciesFor(name)
.filter(dep -> nodeClass.isAssignableFrom(dep.getRight().getProjectNode().getClass()))
.sorted(Comparator.comparing(ImmutablePair::getLeft))
.map(dep -> nodeClass.cast(dep.getRight().getProjectNode()))
.collect(Collectors.toList());
}

private List<ProjectDependency<ProjectNode>> getDependencyCache() {
if (dependencyCache == null) {
dependencyCache = projectFile.getDependencies();
}
return dependencyCache;
}

private Stream<ImmutablePair<String, ProjectDependency<ProjectNode>>> getDependenciesFor(String name) {
Pattern pattern = Pattern.compile(name + "_(\\d+)");
return getDependencyCache()
.stream()
.map(dep -> {
Matcher depMatch = pattern.matcher(dep.getName());
if (depMatch.matches()) {
return ImmutablePair.of(depMatch.group(1), dep);
}
return null;
})
.filter(Objects::nonNull);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void replaceDependency(String oldDependencyId, ProjectNode replacementNod
}).collect(Collectors.toList())));
}

public <T> List<T> getDependencies(String name, Class<T> nodeClass) {
public <T extends ProjectNode> List<T> getDependencies(String name, Class<T> nodeClass) {
Objects.requireNonNull(name);
Objects.requireNonNull(nodeClass);
return storage.getDependencies(info.getId(), name).stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,89 +1,21 @@
/**
* Copyright (c) 2018, RTE (http://www.rte-france.com)
/*
* Copyright (c) 2019, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/

package com.powsybl.afs.ext.base;

import com.google.common.io.CharStreams;
import com.powsybl.afs.ProjectFile;
import com.powsybl.afs.ProjectFileCreationContext;
import com.powsybl.afs.storage.events.AppStorageListener;
import com.powsybl.afs.storage.events.NodeDataUpdated;
import com.powsybl.afs.storage.events.NodeEvent;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
* @author Paul Bui-Quang <paul.buiquang at rte-france.com>
*/
public abstract class AbstractModificationScript extends ProjectFile implements StorableScript {

private static final String NODE_DATA_UPDATED = "NODE_DATA_UPDATED";

private final String scriptContentName;

private final List<ScriptListener> listeners = new ArrayList<>();

private final AppStorageListener l = eventList -> processEvents(eventList.getEvents(), info.getId(), listeners);

@Deprecated
public abstract class AbstractModificationScript extends AbstractScript<AbstractModificationScript> {
public AbstractModificationScript(ProjectFileCreationContext context, int codeVersion, String scriptContentName) {
super(context, codeVersion);
this.scriptContentName = Objects.requireNonNull(scriptContentName);
storage.getEventsBus().addListener(l);
}

private void processEvents(List<NodeEvent> events, String nodeId, List<ScriptListener> listeners) {
for (NodeEvent event : events) {
if (event.getType().equals(NODE_DATA_UPDATED)) {
NodeDataUpdated dataUpdated = (NodeDataUpdated) event;
if (dataUpdated.getId().equals(nodeId) && scriptContentName.equals(dataUpdated.getDataName())) {
for (ScriptListener listener : listeners) {
listener.scriptUpdated();
}
}
}
}
}

@Override
public String readScript() {
try {
return CharStreams.toString(new InputStreamReader(storage.readBinaryData(info.getId(), scriptContentName).orElseThrow(AssertionError::new), StandardCharsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public void writeScript(String content) {
try (Reader reader = new StringReader(content);
Writer writer = new OutputStreamWriter(storage.writeBinaryData(info.getId(), scriptContentName), StandardCharsets.UTF_8)) {
CharStreams.copy(reader, writer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
storage.updateModificationTime(info.getId());
storage.flush();

// invalidate backward dependencies
invalidate();
}

@Override
public void addListener(ScriptListener listener) {
Objects.requireNonNull(listener);
listeners.add(listener);
}

@Override
public void removeListener(ScriptListener listener) {
Objects.requireNonNull(listener);
listeners.remove(listener);
super(context, codeVersion, scriptContentName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/**
* Copyright (c) 2018, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.afs.ext.base;

import com.google.common.io.CharStreams;
import com.powsybl.afs.OrderedDependencyManager;
import com.powsybl.afs.ProjectFile;
import com.powsybl.afs.ProjectFileCreationContext;
import com.powsybl.afs.storage.events.AppStorageListener;
import com.powsybl.afs.storage.events.NodeDataUpdated;
import com.powsybl.afs.storage.events.NodeEvent;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
*/
public abstract class AbstractScript<T extends AbstractScript> extends ProjectFile implements StorableScript {

private static final String NODE_DATA_UPDATED = "NODE_DATA_UPDATED";

private static final String INCLUDED_SCRIPTS_DEPENDENCY_NAME = "scriptIncludes";
private static final String DEFAULT_SCRIPTS_DELIMITER = "\n\n";
private final String scriptContentName;
private final List<ScriptListener> listeners = new ArrayList<>();
private final AppStorageListener l = eventList -> processEvents(eventList.getEvents(), info.getId(), listeners);
protected final OrderedDependencyManager orderedDependencyManager = new OrderedDependencyManager(this);

public AbstractScript(ProjectFileCreationContext context, int codeVersion, String scriptContentName) {
super(context, codeVersion);
this.scriptContentName = Objects.requireNonNull(scriptContentName);
storage.getEventsBus().addListener(l);
}

private void processEvents(List<NodeEvent> events, String nodeId, List<ScriptListener> listeners) {
for (NodeEvent event : events) {
if (NODE_DATA_UPDATED.equals(event.getType())) {
NodeDataUpdated dataUpdated = (NodeDataUpdated) event;
if (dataUpdated.getId().equals(nodeId) && scriptContentName.equals(dataUpdated.getDataName())) {
for (ScriptListener listener : listeners) {
listener.scriptUpdated();
}
}
}
}
}

public List<AbstractScript> getIncludedScripts() {
return orderedDependencyManager.getDependencies(INCLUDED_SCRIPTS_DEPENDENCY_NAME, AbstractScript.class);
}

public void addGenericScript(GenericScript genericScript) {
orderedDependencyManager.appendDependencies(INCLUDED_SCRIPTS_DEPENDENCY_NAME, Collections.singletonList(genericScript));
}

public void addScript(T includeScript) {
orderedDependencyManager.appendDependencies(INCLUDED_SCRIPTS_DEPENDENCY_NAME, Collections.singletonList(includeScript));
}

public void removeScript(String scriptNodeId) {
orderedDependencyManager.removeDependencies(INCLUDED_SCRIPTS_DEPENDENCY_NAME, Collections.singletonList(scriptNodeId));
}

@Override
public String readScript(boolean withIncludes) {
String ownContent = readScript();
if (withIncludes) {
String includesScript = orderedDependencyManager
.getDependencies(INCLUDED_SCRIPTS_DEPENDENCY_NAME, AbstractScript.class)
.stream()
.map(script -> script.readScript(true))
.collect(Collectors.joining(DEFAULT_SCRIPTS_DELIMITER));
if (StringUtils.isNotBlank(includesScript)) {
includesScript += DEFAULT_SCRIPTS_DELIMITER;
}
return includesScript + ownContent;
}
return ownContent;
}

@Override
public String readScript() {
try {
return CharStreams.toString(new InputStreamReader(storage.readBinaryData(info.getId(), scriptContentName).orElseThrow(AssertionError::new), StandardCharsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public void writeScript(String content) {
try (Reader reader = new StringReader(content);
Writer writer = new OutputStreamWriter(storage.writeBinaryData(info.getId(), scriptContentName), StandardCharsets.UTF_8)) {
CharStreams.copy(reader, writer);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
storage.updateModificationTime(info.getId());
storage.flush();

// invalidate backward dependencies
invalidate();
}

@Override
public void addListener(ScriptListener listener) {
Objects.requireNonNull(listener);
listeners.add(listener);
}

@Override
public void removeListener(ScriptListener listener) {
Objects.requireNonNull(listener);
listeners.remove(listener);
}
}
Loading

0 comments on commit caed62f

Please sign in to comment.