-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Improves memory consumption and execution time of online documentation
1 parent
f2d4188
commit 65a7ac0
Showing
5 changed files
with
209 additions
and
101 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
gaml.compiler/src/gaml/compiler/gaml/documentation/DocumentedObject.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package gaml.compiler.gaml.documentation; | ||
|
||
public class DocumentedObject { | ||
|
||
} |
160 changes: 160 additions & 0 deletions
160
gaml.compiler/src/gaml/compiler/gaml/documentation/GamlDocumentationGraph.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
/******************************************************************************************************* | ||
* | ||
* GamlDocumentationGraph.java, in gaml.compiler, is part of the source code of the GAMA modeling and simulation | ||
* platform (v.2024-06). | ||
* | ||
* (c) 2007-2024 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, ESPACE-DEV, CTU) | ||
* | ||
* Visit https://github.com/gama-platform/gama for license information and contacts. | ||
* | ||
********************************************************************************************************/ | ||
package gaml.compiler.gaml.documentation; | ||
|
||
import java.util.Collections; | ||
import java.util.Set; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.function.Function; | ||
|
||
import org.eclipse.emf.common.util.URI; | ||
import org.jgrapht.GraphType; | ||
import org.jgrapht.Graphs; | ||
import org.jgrapht.graph.AbstractBaseGraph; | ||
import org.jgrapht.graph.DefaultGraphType; | ||
import org.jgrapht.graph.EdgeSetFactory; | ||
import org.jgrapht.graph.FastLookupGraphSpecificsStrategy; | ||
import org.jgrapht.graph.IntrusiveEdgesSpecifics; | ||
import org.jgrapht.graph.UniformIntrusiveEdgesSpecifics; | ||
|
||
import gama.dev.DEBUG; | ||
|
||
/** | ||
* The Class GamlResourceGraph. | ||
*/ | ||
public class GamlDocumentationGraph { | ||
|
||
static { | ||
DEBUG.ON(); | ||
} | ||
|
||
/** | ||
* The Class Graph. | ||
*/ | ||
class InternalGraph extends AbstractBaseGraph<URI, Edge> { | ||
|
||
/** | ||
* Instantiates a new graph. | ||
*/ | ||
public InternalGraph() { | ||
super(null, null, | ||
new DefaultGraphType.Builder().undirected().allowMultipleEdges(true).allowSelfLoops(false) | ||
.weighted(false).allowCycles(true).build(), | ||
new FastLookupGraphSpecificsStrategy<URI, Edge>() { | ||
@Override | ||
public Function<GraphType, IntrusiveEdgesSpecifics<URI, Edge>> | ||
getIntrusiveEdgesSpecificsFactory() { | ||
return type -> new UniformIntrusiveEdgesSpecifics<>(new ConcurrentHashMap<>()) { | ||
public URI getEdgeTarget(final Edge e) { | ||
try { | ||
return super.getEdgeTarget(e); | ||
} catch (Exception e1) { | ||
return null; | ||
} | ||
} | ||
}; | ||
|
||
} | ||
|
||
@Override | ||
public EdgeSetFactory<URI, Edge> getEdgeSetFactory() { | ||
return vertex -> Collections.newSetFromMap(new ConcurrentHashMap<Edge, Boolean>()); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public boolean addEdge(final URI sourceVertex, final URI targetVertex, final Edge e) { | ||
addVertex(sourceVertex); | ||
addVertex(targetVertex); | ||
removeEdge(sourceVertex, targetVertex); | ||
return super.addEdge(sourceVertex, targetVertex, e); | ||
} | ||
|
||
} | ||
|
||
/** The regular imports. */ | ||
InternalGraph internal = new InternalGraph(); | ||
|
||
/** | ||
* Reset. | ||
*/ | ||
void reset() { | ||
internal = new InternalGraph(); | ||
} | ||
|
||
/** | ||
* The Class Edge. | ||
*/ | ||
class Edge {} | ||
|
||
/** | ||
* Predecessors of. | ||
* | ||
* @param newURI | ||
* the new URI | ||
* @return the sets the | ||
*/ | ||
public Set<URI> predecessorsOf(final URI uri) { | ||
if (!internal.containsVertex(uri)) return Collections.EMPTY_SET; | ||
|
||
return Graphs.neighborSetOf(internal, uri); | ||
|
||
// Set<Edge> incoming = internal.incomingEdgesOf(uri); | ||
// | ||
// if (incoming.isEmpty()) return Collections.EMPTY_SET; | ||
// Set<URI> result = new HashSet<>(); | ||
// for (Edge edge : incoming) { result.add(Graphs.getOppositeVertex(internal, edge, uri)); } | ||
// return result; | ||
} | ||
|
||
/** | ||
* Successors of. | ||
* | ||
* @param newURI | ||
* the new URI | ||
* @return the sets the | ||
*/ | ||
public Set<URI> successorsOf(final URI uri) { | ||
if (!internal.containsVertex(uri)) return Collections.EMPTY_SET; | ||
return Graphs.neighborSetOf(internal, uri); | ||
// Set<Edge> outgoing = internal.outgoingEdgesOf(uri); | ||
// if (outgoing.isEmpty()) return Collections.EMPTY_SET; | ||
// Set<URI> result = new HashSet<>(); | ||
// for (Edge edge : outgoing) { result.add(Graphs.getOppositeVertex(internal, edge, uri)); } | ||
// return result; | ||
} | ||
|
||
/** | ||
* Adds the edge. | ||
* | ||
* @param from | ||
* the from | ||
* @param to | ||
* the to | ||
* @param label | ||
* the label | ||
*/ | ||
public void addEdge(final URI from, final URI to) { | ||
internal.addEdge(from, to, new Edge()); | ||
} | ||
|
||
/** | ||
* Removes the node. | ||
* | ||
* @param uri | ||
* the uri | ||
*/ | ||
public void removeNode(final URI uri) { | ||
internal.removeVertex(uri); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,35 +44,18 @@ public class GamlResourceDocumenter implements IDocManager { | |
/** The documentation queue. */ | ||
final Map<URI, GamlResourceDocumentationTask> documentationTasks = new ConcurrentHashMap(); | ||
|
||
/** The documentations from EObject to DocumentationNode. Key is the complete URI of the object */ | ||
final Map<URI, DocumentationNode> docIndexedByObjects = new ConcurrentHashMap(); | ||
/** The documented objects. */ | ||
final Map<URI, DocumentedObject> documentedObjects = new ConcurrentHashMap(); | ||
|
||
/** | ||
* The references from Eobject to resources. Key is the complete URI of the object, value is the set of hashcodes of | ||
* the URIs of open resources using this object | ||
*/ | ||
|
||
Map<URI, Set<URI>> resourcesIndexedByObjects = new ConcurrentHashMap(); | ||
|
||
/** | ||
* The references from resources to EObjets. Key is the URI of the resource, value is the set of hashcodes of the | ||
* complete URIs of objects present in this resource (they might belong to other resources) | ||
*/ | ||
|
||
Map<URI, Set<URI>> objectsIndexedByResources = new ConcurrentHashMap(); | ||
|
||
/** | ||
* Adds the arbitrary documentation task. | ||
* The Record DocumentedObject. | ||
* | ||
* @author Alexis Drogoul ([email protected]) | ||
* @param task | ||
* the task | ||
* @date 30 déc. 2023 | ||
* @param node | ||
* the node | ||
* @param resources | ||
* the resources | ||
*/ | ||
public void addDocumentationTask(final URI res, final Runnable run) { | ||
if (run == null || !isTaskValid(res)) return; | ||
getTaskFor(res).add(run); | ||
} | ||
record DocumentedObject(DocumentationNode node, Set<URI> resources) {} | ||
|
||
/** | ||
* Sets the gaml documentation. | ||
|
@@ -90,8 +73,8 @@ public void addDocumentationTask(final URI res, final Runnable run) { | |
*/ | ||
@Override | ||
public void setGamlDocumentation(final URI res, final EObject object, final IGamlDescription desc) { | ||
addDocumentationTask(res, | ||
() -> internalSetGamlDocumentation(res, getCurrentDocGenerationFor(res), object, desc)); | ||
if (!isTaskValid(res)) return; | ||
getTaskFor(res).add(() -> internalSetGamlDocumentation(res, getCurrentDocGenerationFor(res), object, desc)); | ||
} | ||
|
||
/** | ||
|
@@ -111,9 +94,14 @@ boolean internalSetGamlDocumentation(final URI res, final int generation, final | |
try { | ||
if (!isTaskValid(res) || !isValidGeneration(res, generation)) return false; | ||
URI fragment = EcoreUtil.getURI(object); | ||
docIndexedByObjects.put(fragment, new DocumentationNode(desc)); | ||
resourcesIndexedByObjects.computeIfAbsent(fragment, uri -> new HashSet()).add(res); | ||
objectsIndexedByResources.computeIfAbsent(res, uri -> new HashSet()).add(fragment); | ||
DocumentedObject documented = documentedObjects.get(fragment); | ||
if (documented == null) { | ||
documented = new DocumentedObject(new DocumentationNode(desc), new HashSet()); | ||
documentedObjects.put(fragment, documented); | ||
} | ||
documented.resources.add(res); | ||
GamlResourceDocumentationTask task = getTaskFor(res); | ||
task.objects.add(fragment); | ||
return true; | ||
} catch (final RuntimeException e) { | ||
DEBUG.ERR("Error in documenting " + res.lastSegment(), e); | ||
|
@@ -123,16 +111,15 @@ boolean internalSetGamlDocumentation(final URI res, final int generation, final | |
|
||
// To be called once the validation has been done | ||
@Override | ||
public void doDocument(final URI res, final ModelDescription desc, | ||
public void doDocument(final URI res, final ModelDescription model, | ||
final Map<EObject, IGamlDescription> additionalExpressions) { | ||
|
||
GamlResourceDocumentationTask task; | ||
task = getTaskFor(res); | ||
task.incrementGeneration(); | ||
int generation = getCurrentDocGenerationFor(res); | ||
GamlResourceDocumentationTask task = getTaskFor(res); | ||
int generation = task.incrementGeneration(); | ||
task.add(() -> { | ||
internalDoDocument(res, generation, desc); | ||
recursiveDoDocument(res, generation, model); | ||
additionalExpressions.forEach((e, d) -> internalSetGamlDocumentation(res, generation, e, d)); | ||
// Important to do it here, once all the documentation has been produced | ||
model.dispose(); | ||
}); | ||
} | ||
|
||
|
@@ -146,91 +133,47 @@ public void doDocument(final URI res, final ModelDescription desc, | |
* the desc | ||
* @date 31 déc. 2023 | ||
*/ | ||
private boolean internalDoDocument(final URI resource, final int generation, final IDescription desc) { | ||
private boolean recursiveDoDocument(final URI resource, final int generation, final IDescription desc) { | ||
if (desc == null) return false; | ||
final EObject e = desc.getUnderlyingElement(); | ||
if (e == null) return true; // We return true to continue exploring if other descriptions should be documented | ||
if (!internalSetGamlDocumentation(resource, generation, e, desc)) return false; | ||
return desc.visitOwnChildren(d -> internalDoDocument(resource, generation, d)); | ||
return desc.visitOwnChildren(d -> recursiveDoDocument(resource, generation, d)); | ||
} | ||
|
||
@Override | ||
public IGamlDescription getGamlDocumentation(final EObject object) { | ||
if (object == null) return null; | ||
IGamlDescription doc = docIndexedByObjects.get(EcoreUtil.getURI(object)); | ||
// if (doc == null && DEBUG.IS_ON()) { | ||
// DEBUG.OUT("EObject " + object + " in resource " | ||
// + (object.eResource() == null ? "null" : object.eResource().getURI().lastSegment()) | ||
// + " is not documented "); | ||
// } | ||
return doc; | ||
DocumentedObject doc = documentedObjects.get(EcoreUtil.getURI(object)); | ||
return doc == null ? null : doc.node; | ||
} | ||
|
||
@Override | ||
public void invalidate(final URI uri) { | ||
if (uri == null) return; | ||
// Should we do it immediately ? with the following reasoning: | ||
// 1/ If called from closing the editor, no reason to do it at the end and not immediately | ||
// 2/ If called from the erasing of cache in GamlResource, it must be done immediately to allow the new Eobjects | ||
// to not be mixed with the "old" ones | ||
// addDocumentationTask(d -> { | ||
Set<URI> objects = objectsIndexedByResources.remove(uri); | ||
documentationTasks.remove(uri); | ||
GamlResourceDocumentationTask task = documentationTasks.remove(uri); | ||
Set<URI> objects = task == null ? null : task.objects; | ||
if (objects != null) { | ||
objects.forEach(object -> { | ||
Set<URI> resources = resourcesIndexedByObjects.get(object); | ||
DocumentedObject documented = documentedObjects.get(object); | ||
Set<URI> resources = documented == null ? null : documented.resources; | ||
if (resources != null) { | ||
resources.remove(uri); | ||
if (resources.isEmpty()) { | ||
docIndexedByObjects.remove(object); | ||
resourcesIndexedByObjects.remove(object); | ||
} | ||
if (resources.isEmpty()) { documentedObjects.remove(object); } | ||
} | ||
}); | ||
} | ||
// if (DEBUG.IS_ON()) { debugStatistics("Invalidation of " + uri.lastSegment()); } | ||
// }); | ||
|
||
/** | ||
* Debug statistics. | ||
* | ||
* @param title | ||
* the title | ||
*/ | ||
} | ||
|
||
/** | ||
* Debug statistics. | ||
* | ||
* @author Alexis Drogoul ([email protected]) | ||
* @param title | ||
* the title | ||
* @date 31 déc. 2023 | ||
*/ | ||
// private void debugStatistics(final String title) { | ||
// DEBUG.SECTION(title); | ||
// DEBUG.BANNER("DOC", "docIndexedByObjects", "size", String.valueOf(docIndexedByObjects.size())); | ||
// DEBUG.BANNER("DOC", "eObjectsIndexedByResources", "size", String.valueOf(objectsIndexedByResources.size())); | ||
// DEBUG.BANNER("DOC", "Opened Resources", "names", new HashSet(resourceNames.values()).toString()); | ||
// DEBUG.BANNER("DOC", "resourcesIndexedByEObjects", "size", String.valueOf(resourcesIndexedByObjects.size())); | ||
// DEBUG.LINE(); | ||
// /** | ||
// * Invalidate all. | ||
// */ | ||
// } | ||
|
||
/** | ||
* Invalidate all. | ||
* | ||
* @author Alexis Drogoul ([email protected]) | ||
* @date 30 déc. 2023 | ||
*/ | ||
public void invalidateAll() { | ||
// if (DEBUG.IS_ON()) { debugStatistics("Before clean build"); } | ||
getTaskFor(URI.createURI("")).add(() -> { | ||
docIndexedByObjects.clear(); | ||
resourcesIndexedByObjects.clear(); | ||
objectsIndexedByResources.clear(); | ||
documentedObjects.clear(); | ||
documentationTasks.clear(); | ||
}); | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters