org.gradle.model.internal.registry.ModelGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.model.internal.registry;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import org.gradle.api.Nullable;
import org.gradle.model.internal.core.ModelNode;
import org.gradle.model.internal.core.ModelPath;
import java.util.*;
class ModelGraph {
private enum PendingState {
ADD, NOTIFY
}
private final ModelNodeInternal root;
private final Map flattened = Maps.newTreeMap();
private final SetMultimap pathListeners = LinkedHashMultimap.create();
private final SetMultimap parentListeners = LinkedHashMultimap.create();
private final SetMultimap ancestorListeners = LinkedHashMultimap.create();
private final Set listeners = new LinkedHashSet();
private boolean notifying;
private final List pendingListeners = new ArrayList();
private final Map pendingNodes = Maps.newLinkedHashMap();
public ModelGraph(ModelNodeInternal rootNode) {
this.root = rootNode;
flattened.put(root.getPath(), root);
}
public ModelNodeInternal getRoot() {
return root;
}
public Map getFlattened() {
return Collections.unmodifiableMap(flattened);
}
public void add(ModelNodeInternal node) {
if (notifying) {
pendingNodes.put(node, PendingState.ADD);
return;
}
doAdd(node);
flush();
}
public void nodeDiscovered(ModelNodeInternal node) {
if (notifying) {
if (!pendingNodes.containsKey(node)) {
pendingNodes.put(node, PendingState.NOTIFY);
}
return;
}
doNotify(node);
flush();
}
private void doAdd(ModelNodeInternal node) {
flattened.put(node.getPath(), node);
if (node.isAtLeast(ModelNode.State.Discovered)) {
doNotify(node);
}
}
private void doNotify(ModelNodeInternal node) {
notifying = true;
try {
notifyListeners(node, pathListeners.get(node.getPath()));
notifyListeners(node, parentListeners.get(node.getPath().getParent()));
notifyListeners(node, listeners);
if (!ancestorListeners.isEmpty()) {
// Don't traverse path back to root when there is nothing that can possibly match
for (ModelPath path = node.getPath().getParent(); path != null; path = path.getParent()) {
notifyListeners(node, ancestorListeners.get(path));
}
}
} finally {
notifying = false;
}
}
private void notifyListeners(ModelNodeInternal node, Iterable listeners) {
for (ModelListener listener : listeners) {
maybeNotify(node, listener);
}
}
public void addListener(ModelListener listener) {
if (notifying) {
pendingListeners.add(listener);
return;
}
doAddListener(listener);
flush();
}
private void doAddListener(ModelListener listener) {
notifying = true;
try {
if (listener.getPath() != null) {
addPathListener(listener);
return;
}
if (listener.getParent() != null) {
addParentListener(listener);
return;
}
if (listener.getAncestor() != null) {
addAncestorListener(listener);
return;
}
addEverythingListener(listener);
} finally {
notifying = false;
}
}
private void addEverythingListener(ModelListener listener) {
for (ModelNodeInternal node : flattened.values()) {
maybeNotify(node, listener);
}
listeners.add(listener);
}
private void addAncestorListener(ModelListener listener) {
if (ModelPath.ROOT.equals(listener.getAncestor())) {
// Don't need to match on path
addEverythingListener(listener);
return;
}
ModelNodeInternal ancestor = flattened.get(listener.getAncestor());
if (ancestor != null) {
Deque queue = new ArrayDeque();
queue.add(ancestor);
while (!queue.isEmpty()) {
ModelNodeInternal parent = queue.removeFirst();
for (ModelNodeInternal node : parent.getLinks()) {
maybeNotify(node, listener);
queue.addFirst(node);
}
}
}
ancestorListeners.put(listener.getAncestor(), listener);
}
private void addParentListener(ModelListener listener) {
ModelNodeInternal parent = flattened.get(listener.getParent());
if (parent != null) {
for (ModelNodeInternal node : parent.getLinks()) {
maybeNotify(node, listener);
}
}
parentListeners.put(listener.getParent(), listener);
}
private void addPathListener(ModelListener listener) {
ModelNodeInternal node = flattened.get(listener.getPath());
if (node != null) {
maybeNotify(node, listener);
}
pathListeners.put(listener.getPath(), listener);
}
private void flush() {
while (!pendingListeners.isEmpty()) {
doAddListener(pendingListeners.remove(0));
}
while (!pendingNodes.isEmpty()) {
Iterator> iPendingNodes = pendingNodes.entrySet().iterator();
Map.Entry entry = iPendingNodes.next();
iPendingNodes.remove();
ModelNodeInternal pendingNode = entry.getKey();
switch (entry.getValue()) {
case ADD:
doAdd(pendingNode);
break;
case NOTIFY:
doNotify(pendingNode);
break;
}
}
}
private void maybeNotify(ModelNodeInternal node, ModelListener listener) {
if (!node.isAtLeast(ModelNode.State.Discovered)) {
return;
}
listener.onDiscovered(node);
}
@Nullable
public ModelNodeInternal find(ModelPath path) {
return flattened.get(path);
}
public ModelNodeInternal get(ModelPath path) {
ModelNodeInternal found = find(path);
if (found == null) {
throw new IllegalStateException("Expected model node @ '" + path + "' but none was found");
}
return found;
}
@Nullable
public ModelNodeInternal remove(ModelNode node) {
ModelNodeInternal parentNode = find(node.getPath().getParent());
if (parentNode != null) {
parentNode.removeLink(node.getPath().getName());
}
return flattened.remove(node.getPath());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy