mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Merge remote-tracking branch 'origin/GP-1969_Dan_traceModelBrowser--SQUASHED'
This commit is contained in:
commit
00dbd26511
59 changed files with 6131 additions and 269 deletions
|
@ -277,7 +277,7 @@ public class DBTraceObjectBreakpointLocation
|
|||
}
|
||||
|
||||
PathMatcher procMatcher = schema.searchFor(TargetProcess.class, false);
|
||||
return object.getAncestors(getLifespan(), procMatcher)
|
||||
return object.getAncestorsRoot(getLifespan(), procMatcher)
|
||||
.flatMap(proc -> proc.getSource(object)
|
||||
.querySuccessorsInterface(getLifespan(),
|
||||
TraceObjectThread.class))
|
||||
|
|
|
@ -203,6 +203,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
|
||||
@Override
|
||||
public RangeSet<Long> getLife() {
|
||||
// TODO: This should really be cached
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
RangeSet<Long> result = TreeRangeSet.create();
|
||||
// NOTE: connected ranges should already be coalesced
|
||||
|
@ -220,19 +221,20 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
return manager.doGetObject(path.parent());
|
||||
}
|
||||
|
||||
protected void doInsert(Range<Long> lifespan, ConflictResolution resolution) {
|
||||
protected DBTraceObjectValPath doInsert(Range<Long> lifespan, ConflictResolution resolution) {
|
||||
if (path.isRoot()) {
|
||||
return;
|
||||
return DBTraceObjectValPath.of();
|
||||
}
|
||||
DBTraceObject parent = doCreateCanonicalParentObject();
|
||||
parent.setValue(lifespan, path.key(), this, resolution);
|
||||
parent.doInsert(lifespan, resolution);
|
||||
InternalTraceObjectValue value = parent.setValue(lifespan, path.key(), this, resolution);
|
||||
DBTraceObjectValPath path = parent.doInsert(lifespan, resolution);
|
||||
return path.append(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(Range<Long> lifespan, ConflictResolution resolution) {
|
||||
public DBTraceObjectValPath insert(Range<Long> lifespan, ConflictResolution resolution) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
doInsert(lifespan, resolution);
|
||||
return doInsert(lifespan, resolution);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +255,9 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
}
|
||||
|
||||
protected void doRemoveTree(Range<Long> span) {
|
||||
for (DBTraceObjectValue parent : getParents()) {
|
||||
parent.doTruncateOrDeleteAndEmitLifeChange(span);
|
||||
}
|
||||
for (InternalTraceObjectValue value : getValues()) {
|
||||
value.doTruncateOrDeleteAndEmitLifeChange(span);
|
||||
if (value.isCanonical()) {
|
||||
|
@ -264,7 +269,6 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
@Override
|
||||
public void removeTree(Range<Long> span) {
|
||||
try (LockHold hold = manager.trace.lockWrite()) {
|
||||
getCanonicalParents(span).forEach(v -> v.doTruncateOrDeleteAndEmitLifeChange(span));
|
||||
doRemoveTree(span);
|
||||
}
|
||||
}
|
||||
|
@ -327,10 +331,14 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
return ifCls.cast(ifaces.get(ifCls));
|
||||
}
|
||||
|
||||
protected Collection<? extends DBTraceObjectValue> doGetParents() {
|
||||
return manager.valuesByChild.get(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends DBTraceObjectValue> getParents() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return manager.valuesByChild.get(this);
|
||||
return doGetParents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,23 +634,35 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getAncestors(
|
||||
public Stream<? extends TraceObjectValPath> getAncestors(Range<Long> span,
|
||||
PathPredicates relativePredicates) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
Stream<? extends DBTraceObjectValPath> ancestors =
|
||||
doStreamVisitor(span, new InternalAncestorsRelativeVisitor(relativePredicates));
|
||||
if (relativePredicates.matches(List.of())) {
|
||||
return Stream.concat(Stream.of(DBTraceObjectValPath.of()), ancestors);
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getAncestorsRoot(
|
||||
Range<Long> span, PathPredicates rootPredicates) {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return doStreamVisitor(span, new InternalAncestorsVisitor(rootPredicates));
|
||||
return doStreamVisitor(span, new InternalAncestorsRootVisitor(rootPredicates));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends DBTraceObjectValPath> getSuccessors(
|
||||
Range<Long> span, PathPredicates relativePredicates) {
|
||||
DBTraceObjectValPath empty = DBTraceObjectValPath.of();
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
Stream<? extends DBTraceObjectValPath> succcessors =
|
||||
doStreamVisitor(span, new InternalSuccessorsVisitor(relativePredicates));
|
||||
doStreamVisitor(span, new InternalSuccessorsRelativeVisitor(relativePredicates));
|
||||
if (relativePredicates.matches(List.of())) {
|
||||
// Pre-cat the empty path (not the empty stream)
|
||||
return Stream.concat(Stream.of(empty), succcessors);
|
||||
return Stream.concat(Stream.of(DBTraceObjectValPath.of()), succcessors);
|
||||
}
|
||||
return succcessors;
|
||||
}
|
||||
|
@ -794,7 +814,7 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
|
|||
Class<? extends TargetObject> targetIf) {
|
||||
// This is a sort of meet-in-the-middle. The type search must originate from the root
|
||||
PathMatcher matcher = getManager().getRootSchema().searchFor(targetIf, false);
|
||||
return getAncestors(span, matcher);
|
||||
return getAncestorsRoot(span, matcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,7 @@ import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
|
|||
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
|
||||
import ghidra.trace.database.target.DBTraceObjectValue.DBTraceObjectDBFieldCodec;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.util.TraceAddressSpace;
|
||||
import ghidra.util.LockHold;
|
||||
|
@ -66,6 +67,12 @@ public class DBTraceObjectAddressRangeValue
|
|||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": parent=" + parent + ", key=" + entryKey +
|
||||
", lifespan=" + getLifespan() + ", value=" + getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setRecordValue(DBTraceObjectAddressRangeValue value) {
|
||||
// Nothing to do. I am the value
|
||||
|
@ -121,11 +128,23 @@ public class DBTraceObjectAddressRangeValue
|
|||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isObject() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getChildOrNull() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectKeyPath getCanonicalPath() {
|
||||
try (LockHold hold = manager.trace.lockRead()) {
|
||||
return parent.getCanonicalPath().extend(entryKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanonical() {
|
||||
return false;
|
||||
|
|
|
@ -368,7 +368,7 @@ public class DBTraceObjectManager implements TraceObjectManager, DBTraceManager
|
|||
if (rootVal == null) {
|
||||
return Stream.of();
|
||||
}
|
||||
return rootVal.doStreamVisitor(span, new InternalSuccessorsVisitor(predicates));
|
||||
return rootVal.doStreamVisitor(span, new InternalSuccessorsRelativeVisitor(predicates));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.trace.database.DBTraceUtils;
|
|||
import ghidra.trace.database.target.InternalTreeTraversal.Visitor;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.util.LockHold;
|
||||
import ghidra.util.database.*;
|
||||
import ghidra.util.database.DBCachedObjectStoreFactory.AbstractDBFieldCodec;
|
||||
|
@ -275,6 +276,11 @@ public class DBTraceObjectValue extends DBAnnotatedObject implements InternalTra
|
|||
return (DBTraceObject) getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isObject() {
|
||||
return child != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject getChildOrNull() {
|
||||
return child;
|
||||
|
@ -320,6 +326,10 @@ public class DBTraceObjectValue extends DBAnnotatedObject implements InternalTra
|
|||
return InternalTreeTraversal.INSTANCE.walkValue(visitor, this, span, null);
|
||||
}
|
||||
|
||||
protected TraceObjectKeyPath doGetCanonicalPath() {
|
||||
return triple.parent.getCanonicalPath().extend(triple.key);
|
||||
}
|
||||
|
||||
protected boolean doIsCanonical() {
|
||||
if (child == null) {
|
||||
return false;
|
||||
|
@ -327,7 +337,14 @@ public class DBTraceObjectValue extends DBAnnotatedObject implements InternalTra
|
|||
if (triple.parent == null) {
|
||||
return true;
|
||||
}
|
||||
return triple.parent.getCanonicalPath().extend(triple.key).equals(child.getCanonicalPath());
|
||||
return doGetCanonicalPath().equals(child.getCanonicalPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraceObjectKeyPath getCanonicalPath() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
return doGetCanonicalPath();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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 ghidra.trace.database.target;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.trace.database.target.InternalTreeTraversal.SpanIntersectingVisitor;
|
||||
import ghidra.trace.database.target.InternalTreeTraversal.VisitResult;
|
||||
|
||||
public class InternalAncestorsRelativeVisitor implements SpanIntersectingVisitor {
|
||||
|
||||
protected final PathPredicates predicates;
|
||||
|
||||
public InternalAncestorsRelativeVisitor(PathPredicates predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObjectValPath composePath(DBTraceObjectValPath pre,
|
||||
InternalTraceObjectValue value) {
|
||||
return pre == null ? DBTraceObjectValPath.of() : pre.prepend(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitResult visitValue(InternalTraceObjectValue value, DBTraceObjectValPath path) {
|
||||
List<String> keyList = path.getKeyList();
|
||||
return VisitResult.result(predicates.matches(keyList),
|
||||
predicates.ancestorCouldMatchRight(keyList, true) && value.getChildOrNull() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBTraceObject continueObject(InternalTraceObjectValue value) {
|
||||
return value.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends InternalTraceObjectValue> continueValues(DBTraceObject object,
|
||||
Range<Long> span, DBTraceObjectValPath pre) {
|
||||
Set<String> prevKeys = predicates.getPrevKeys(pre.getKeyList());
|
||||
if (prevKeys.isEmpty()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
return object.doGetParents()
|
||||
.stream()
|
||||
.filter(v -> PathPredicates.anyMatches(prevKeys, v.getEntryKey()));
|
||||
}
|
||||
}
|
|
@ -23,11 +23,11 @@ import ghidra.dbg.util.PathPredicates;
|
|||
import ghidra.trace.database.target.InternalTreeTraversal.SpanIntersectingVisitor;
|
||||
import ghidra.trace.database.target.InternalTreeTraversal.VisitResult;
|
||||
|
||||
public class InternalAncestorsVisitor implements SpanIntersectingVisitor {
|
||||
public class InternalAncestorsRootVisitor implements SpanIntersectingVisitor {
|
||||
|
||||
protected final PathPredicates predicates;
|
||||
|
||||
public InternalAncestorsVisitor(PathPredicates predicates) {
|
||||
public InternalAncestorsRootVisitor(PathPredicates predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
|
@ -26,11 +26,11 @@ import ghidra.trace.database.DBTraceUtils;
|
|||
import ghidra.trace.database.target.InternalTreeTraversal.SpanIntersectingVisitor;
|
||||
import ghidra.trace.database.target.InternalTreeTraversal.VisitResult;
|
||||
|
||||
public class InternalSuccessorsVisitor implements SpanIntersectingVisitor {
|
||||
public class InternalSuccessorsRelativeVisitor implements SpanIntersectingVisitor {
|
||||
|
||||
protected final PathPredicates predicates;
|
||||
|
||||
public InternalSuccessorsVisitor(PathPredicates predicates) {
|
||||
public InternalSuccessorsRelativeVisitor(PathPredicates predicates) {
|
||||
this.predicates = predicates;
|
||||
}
|
||||
|
|
@ -125,6 +125,14 @@ public class TraceDomainObjectListener implements DomainObjectListener {
|
|||
typedMap.put(type, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for the given event, taking the affected object, the old value, and the new value
|
||||
*
|
||||
* @param <T> the type of the affected object
|
||||
* @param <U> the type of the values
|
||||
* @param type the event type
|
||||
* @param handler the handler
|
||||
*/
|
||||
protected <T, U> void listenFor(TraceChangeType<T, U> type,
|
||||
AffectedAndValuesOnlyHandler<? super T, ? super U> handler) {
|
||||
typedMap.put(type, handler);
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.stream.Stream;
|
|||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.RangeSet;
|
||||
|
||||
import ghidra.dbg.target.TargetMethod;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
|
@ -38,6 +39,8 @@ import ghidra.trace.model.TraceUniqueObject;
|
|||
* In many cases, such interfaces are just wrappers.
|
||||
*/
|
||||
public interface TraceObject extends TraceUniqueObject {
|
||||
String EXTRA_INTERFACES_ATTRIBUTE_NAME = "_extra_ifs";
|
||||
|
||||
/**
|
||||
* Get the trace containing this object
|
||||
*
|
||||
|
@ -79,8 +82,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
*
|
||||
* @param the minimum lifespan of edges from the root to this object
|
||||
* @param resolution the rule for handling duplicate keys when setting values.
|
||||
* @return the value path from root to the newly inserted object
|
||||
*/
|
||||
void insert(Range<Long> lifespan, ConflictResolution resolution);
|
||||
TraceObjectValPath insert(Range<Long> lifespan, ConflictResolution resolution);
|
||||
|
||||
/**
|
||||
* Remove this object from its canonical path for the given lifespan
|
||||
|
@ -97,9 +101,9 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* Remove this object and its successors from their canonical paths for the given span
|
||||
*
|
||||
* <p>
|
||||
* Truncate the lifespans of this object's canonical parent value and all canonical values
|
||||
* succeeding this object. If a truncated value's lifespan is contained in the given span, the
|
||||
* value will be deleted.
|
||||
* Truncate the lifespans of this object's parent values and all canonical values succeeding
|
||||
* this object. If a truncated value's lifespan is contained in the given span, the value will
|
||||
* be deleted.
|
||||
*
|
||||
* @param span the span during which this object and its canonical successors should be removed
|
||||
*/
|
||||
|
@ -282,9 +286,20 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
* @param rootPredicates the predicates for matching path keys, relative to the root
|
||||
* @return the stream of matching paths to values
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getAncestors(Range<Long> span,
|
||||
Stream<? extends TraceObjectValPath> getAncestorsRoot(Range<Long> span,
|
||||
PathPredicates rootPredicates);
|
||||
|
||||
/**
|
||||
* Stream all ancestor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
*
|
||||
* @param span a span which values along the path must intersect
|
||||
* @param relativePredicates the predicates for matching path keys, relative to this object
|
||||
* @return the stream of matching paths to values
|
||||
*/
|
||||
Stream<? extends TraceObjectValPath> getAncestors(Range<Long> span,
|
||||
PathPredicates relativePredicates);
|
||||
|
||||
/**
|
||||
* Stream all successor values of this object matching the given predicates, intersecting the
|
||||
* given span
|
||||
|
@ -466,4 +481,30 @@ public interface TraceObject extends TraceUniqueObject {
|
|||
*/
|
||||
@Override
|
||||
boolean isDeleted();
|
||||
|
||||
/**
|
||||
* Check if the child represents a method at the given snap
|
||||
*
|
||||
* @param snap the snap
|
||||
* @return true if a method
|
||||
*/
|
||||
default boolean isMethod(long snap) {
|
||||
if (getTargetSchema().getInterfaces().contains(TargetMethod.class)) {
|
||||
return true;
|
||||
}
|
||||
TraceObjectValue extras = getAttribute(snap, TraceObject.EXTRA_INTERFACES_ATTRIBUTE_NAME);
|
||||
if (extras == null) {
|
||||
return false;
|
||||
}
|
||||
Object val = extras.getValue();
|
||||
if (!(val instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
String valStr = (String) val;
|
||||
// Not ideal, but it's not a substring of any other schema interface....
|
||||
if (valStr.contains("Method")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,9 +225,26 @@ public final class TraceObjectKeyPath implements Comparable<TraceObjectKeyPath>
|
|||
if (!predicates.ancestorMatches(keyList, false)) {
|
||||
return Stream.of();
|
||||
}
|
||||
Stream<TraceObjectKeyPath> ancestry =
|
||||
isRoot() ? Stream.of() : parent().streamMatchingAncestry(predicates);
|
||||
if (predicates.matches(keyList)) {
|
||||
return Stream.concat(Stream.of(this), parent().streamMatchingAncestry(predicates));
|
||||
return Stream.concat(Stream.of(this), ancestry);
|
||||
}
|
||||
return parent().streamMatchingAncestry(predicates);
|
||||
return ancestry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this path is an ancestor of the given path
|
||||
*
|
||||
* <p>
|
||||
* Equivalently, check if the given path is a successor of this path. A path is considered an
|
||||
* ancestor of itself. To check for a strict ancestor, use
|
||||
* {@code this.isAncestor(that) && !this.equals(that)}.
|
||||
*
|
||||
* @param that the supposed successor to this path
|
||||
* @return true if the given path is in fact a successor
|
||||
*/
|
||||
public boolean isAncestor(TraceObjectKeyPath that) {
|
||||
return PathUtils.isAncestor(keyList, that.keyList);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.trace.model.target;
|
|||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
|
||||
|
@ -43,6 +44,17 @@ public interface TraceObjectValue {
|
|||
*/
|
||||
String getEntryKey();
|
||||
|
||||
/**
|
||||
* Get the "canonical path" of this value
|
||||
*
|
||||
* <p>
|
||||
* This is the parent's canonical path extended by this value's entry key. Note, in the case
|
||||
* this value has a child object, this is not necessarily its canonical path.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TraceObjectKeyPath getCanonicalPath();
|
||||
|
||||
/**
|
||||
* Get the value
|
||||
*
|
||||
|
@ -58,6 +70,13 @@ public interface TraceObjectValue {
|
|||
*/
|
||||
TraceObject getChild();
|
||||
|
||||
/**
|
||||
* Check if the value is an object (i.e., {@link TraceObject})
|
||||
*
|
||||
* @return true if an object, false otherwise
|
||||
*/
|
||||
boolean isObject();
|
||||
|
||||
/**
|
||||
* Check if this value represents its child's canonical location
|
||||
*
|
||||
|
@ -69,6 +88,15 @@ public interface TraceObjectValue {
|
|||
*/
|
||||
boolean isCanonical();
|
||||
|
||||
/**
|
||||
* Get the (target) schema for the value
|
||||
*
|
||||
* @return the schema
|
||||
*/
|
||||
default TargetObjectSchema getTargetSchema() {
|
||||
return getParent().getTargetSchema().getChildSchema(getEntryKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lifespan of this entry, truncating duplicates
|
||||
*
|
||||
|
@ -157,4 +185,17 @@ public interface TraceObjectValue {
|
|||
* if a second is created.
|
||||
*/
|
||||
TraceObjectValue truncateOrDelete(Range<Long> span);
|
||||
|
||||
/**
|
||||
* Check if the schema designates this value as hidden
|
||||
*
|
||||
* @return true if hidden
|
||||
*/
|
||||
default boolean isHidden() {
|
||||
TraceObject parent = getParent();
|
||||
if (parent == null) {
|
||||
return false;
|
||||
}
|
||||
return parent.getTargetSchema().isHidden(getEntryKey());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue