All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jdk.graal.compiler.graphio.parsing.model.GraphDocument Maven / Gradle / Ivy
/*
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.graal.compiler.graphio.parsing.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
public class GraphDocument extends Properties.Entity implements ChangedEventProvider, Folder, FolderElement,
DumpedElement, Properties.MutableOwner {
private final List elements;
private final ChangedEvent changedEvent;
private final ChangedEvent propertyEvent;
private final List dataListeners = new ArrayList<>();
private boolean modified;
// @GuardedBy(this)
private volatile boolean trackModified;
// @GuardedBy(this)
private DocumentLock lock;
private Object documentId;
/**
* Mutable properties from individual objects in the document. The objects may be garbage -
* collected eventually, so their changed Properties would be lost. This field preserves the
* changed values,
*/
private final Map mutableProperties = new HashMap<>();
@SuppressWarnings("this-escape")
public GraphDocument() {
this.elements = new ArrayList<>();
this.changedEvent = new ChangedEvent<>(this);
this.propertyEvent = new ChangedEvent<>(this);
}
@Override
public Object getID() {
return documentId;
}
public void setDocumentId(Object documentId) {
this.documentId = documentId;
}
public void clear() {
synchronized (this) {
elements.clear();
}
changedEvent.fire();
}
@Override
public Properties writableProperties() {
return getProperties();
}
@Override
public void updateProperties(Properties props) {
// document properties are always read-write, so no need to replace.
setModified(true);
propertyEvent.fire();
}
/**
* Determines if the document has been changed.
*
* @return true, if the document has been changed.
*/
public boolean isModified() {
return modified;
}
/**
* Marks the document as (un)modified. If the status changes, the
* {@link #getPropertyChangedEvent()} will be also fired.
*
* @param mod true if the document should become modified.
*/
protected void setModified(boolean mod) {
synchronized (this) {
if (mod == modified) {
return;
}
modified = mod;
}
propertyEvent.fire();
}
public Properties getModifiedProperties(FolderElement item) {
synchronized (this) {
return mutableProperties.get(item);
}
}
protected void notifyPropertiesChanged(FolderElement item, Properties writableProps) {
synchronized (this) {
Properties oldValue = mutableProperties.put(item, writableProps);
if (oldValue != null && oldValue != writableProps) {
throw new IllegalArgumentException();
}
}
setModified(true);
}
@Override
public final ChangedEvent getChangedEvent() {
return changedEvent;
}
public void addGraphDocument(GraphDocument document) {
if (document == this) {
return;
}
changedEvent.beginAtomic();
try {
List extends FolderElement> otherElems = document.getElements();
synchronized (this) {
for (FolderElement e : otherElems) {
e.setParent(this);
this.addElement(e);
}
}
document.clear();
} finally {
changedEvent.endAtomic();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("GraphDocument: ").append(getProperties().toString()).append(" \n\n");
for (FolderElement g : getElements()) {
sb.append(g.toString());
sb.append("\n\n");
}
return sb.toString();
}
public int getSize() {
synchronized (this) {
return elements.size();
}
}
@Override
public List extends FolderElement> getElements() {
synchronized (this) {
return List.copyOf(elements);
}
}
@Override
public void removeElement(FolderElement element) {
synchronized (this) {
if (!elements.remove(element)) {
return;
}
}
changedEvent.fire();
fireDataRemoved(Collections.singleton(element));
}
@Override
public void addElement(FolderElement element) {
boolean propertiesChanged = false;
synchronized (this) {
// note that element.setParent(this) is not called; this is because
// the GraphDocument is used to group selected, possibly unrelated Groups/Graphs when
// saving or processing in bulk.
// setParent must be called explicitly
elements.add(element);
String n = getName();
if ((n == null || n.isEmpty()) && (element instanceof Properties.Entity)) {
for (Property> p : ((Properties.Entity) element).getProperties()) {
if (!KnownPropertyNames.PROPNAME_NAME.equals(p.getName())) {
getProperties().setProperty(p.getName(), p.getValue());
}
}
propertiesChanged = true;
}
}
changedEvent.fire();
if (propertiesChanged) {
propertyEvent.fire();
}
fireDataAdded(Collections.singletonList(element));
}
void fireDataAdded(List extends FolderElement> items) {
DataCollectionEvent ev = new DataCollectionEvent(this, items, true, this);
fireDataCollectionEvent(ev, DataCollectionListener::dataLoaded);
}
public void addDataCollectionListener(DataCollectionListener l) {
synchronized (this) {
dataListeners.add(l);
}
}
public void removeDataCollectionListener(DataCollectionListener l) {
synchronized (this) {
dataListeners.remove(l);
}
}
private void fireDataCollectionEvent(DataCollectionEvent ev, BiConsumer fn) {
DataCollectionListener[] ll = null;
if (trackModified) {
setModified(true);
}
synchronized (this) {
if (dataListeners.isEmpty()) {
return;
}
ll = dataListeners.toArray(new DataCollectionListener[0]);
}
for (DataCollectionListener l : ll) {
fn.accept(l, ev);
}
}
void fireDataRemoved(Collection extends FolderElement> data) {
if (data == null || data.isEmpty()) {
return;
}
fireDataCollectionEvent(new DataCollectionEvent(this, data, this), DataCollectionListener::dataRemoved);
setModified(true);
}
@Override
public boolean isParentOf(FolderElement child) {
return child.getOwner() == this;
}
@Override
public Folder getParent() {
return null;
}
@Override
public String getName() {
return getProperties().getString(KnownPropertyNames.PROPNAME_NAME, null);
}
@Override
public void setParent(Folder parent) {
if (parent != null) {
throw new IllegalStateException("Unsupported");
}
}
@Override
public ChangedEvent getPropertyChangedEvent() {
return propertyEvent;
}
/**
* Informs that the document is locked for modifications. The exception carries a description of
* the lock owner operation, and can be used to break the lock.
*/
@SuppressWarnings("serial")
public static final class LockedException extends RuntimeException {
private final DocumentLock theLock;
public LockedException(String message, DocumentLock theLock) {
super(message);
this.theLock = theLock;
}
public String getLockingOperation() {
return theLock.getOperationLabel();
}
public boolean tryBreakLock() {
return theLock.cancel();
}
}
/**
* Locks document for writing for one operation. The lock is NOT reentrant.
*
* @param operationLabel user-readable label of the lock owner.
* @param cancel optional; callback to cancel the operation
* @return lock instance
*/
public synchronized DocumentLock writeLock(String operationLabel, Callable cancel) {
if (lock != null) {
throw new LockedException("Document is locked by " + lock.getOperationLabel(), lock);
}
return lock = new DocumentLock(operationLabel, cancel);
}
/**
* Represents a lock on the document. During the lock, no other modifications are permitted
* (throw a {@link LockedException}). Lock owner may disable modification tracking, i.e. during
* load from file, the GraphDocument does not become modified.
*
* Modification tracking is restored when the lock is released.
*/
public final class DocumentLock implements AutoCloseable {
private final String operationLabel;
private final Callable cancelFunc;
private boolean unlocked;
public DocumentLock(String operationLabel, Callable cancelFunc) {
this.operationLabel = operationLabel;
this.cancelFunc = cancelFunc;
}
public String getOperationLabel() {
return operationLabel;
}
@Override
public void close() {
unlock();
}
/**
* Unlocks the document. Unlock may be called multiple times after a successful unlock.
*/
public void unlock() {
synchronized (GraphDocument.this) {
if (unlocked) {
return;
}
if (lock != this) {
throw new IllegalArgumentException("Not locked.");
}
unlocked = true;
trackModified = false;
lock = null;
}
}
/**
* Disables modified status tracking for add/remove operations.
*
* @param track enable/disable tracking
*/
public void trackModifications(boolean track) {
if (unlocked) {
throw new IllegalStateException("No longer locked.");
}
trackModified = track;
}
boolean cancel() {
synchronized (GraphDocument.this) {
if (lock != this) {
return true;
}
}
try {
boolean status = cancelFunc.call();
if (!status) {
return false;
}
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
synchronized (GraphDocument.this) {
return lock != this;
}
}
}
}