All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hawkular.inventory.paths.CanonicalPath Maven / Gradle / Ivy

There is a newer version: 0.9.8.Final
Show newest version
/*
 * Copyright 2014-2016 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * 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.hawkular.inventory.paths;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

/**
 * A path represents the canonical traversal to an element through the inventory graph. The canonical traversal
 * always starts at a tenant and follows only the "contains" relationships down to the entity in question. For
 * relationships the "traversal" comprises of merely referencing the relationship by its id.
 *
 * 

For description of the basic behavior and serialized form of the path, please consult {@link Path}. * * @author Lukas Krejci * @since 0.2.0 */ @ApiModel(value = "CanonicalPath", description = "A canonical path is slash-separated list of path segments that uniquely identity an entity" + " in the Hawkular Inventory graph. The path follows the \"contains\" relationships in inventory (which form" + " a tree structure). Each segment specifies the type of the element on that position in the tree, followed by" + " semicolon and the ID of the element. An example of a canonical path would be" + " \"/t;tenant/f;my-feed/r;my-resource\". The type is one of 't' (tenant), 'e' (environment)," + " 'rt' (resource type), 'mt' (metric type), 'ot' (operation type), 'mp' (metadata pack), 'r' (resource)," + " 'm' (metric), 'd' (data) or 'rl' (relationship). Please consult Hawkular Inventory documentation for a more" + " thorough discussion of the different types of entities and their places in the model.", parent = Path.class) public final class CanonicalPath extends Path implements Iterable, Serializable { private static final long serialVersionUID = -333891787878559703L; static final Map> VALID_PROGRESSIONS = new HashMap<>(); static { VALID_PROGRESSIONS.put(SegmentType.t, EnumSet.of(SegmentType.e, SegmentType.mt, SegmentType.rt, SegmentType.f, SegmentType.mp)); VALID_PROGRESSIONS.put(SegmentType.e, EnumSet.of(SegmentType.m, SegmentType.r)); VALID_PROGRESSIONS.put(SegmentType.f, EnumSet.of(SegmentType.m, SegmentType.r, SegmentType.mt, SegmentType.rt)); VALID_PROGRESSIONS.put(SegmentType.rt, EnumSet.of(SegmentType.d, SegmentType.ot)); VALID_PROGRESSIONS.put(SegmentType.ot, EnumSet.of(SegmentType.d)); VALID_PROGRESSIONS.put(SegmentType.r, EnumSet.of(SegmentType.r, SegmentType.d, SegmentType.m)); VALID_PROGRESSIONS.put(SegmentType.d, EnumSet.of(SegmentType.sd)); VALID_PROGRESSIONS.put(SegmentType.sd, EnumSet.of(SegmentType.sd)); VALID_PROGRESSIONS.put(null, EnumSet.of(SegmentType.t, SegmentType.rl)); } /** * JAXB support */ @SuppressWarnings("unused") private CanonicalPath() { } CanonicalPath(int myIdx, List path) { super(0, myIdx, path); } /** * this is here only to cooperate nicely with AbstractPath */ CanonicalPath(int startIdx, int myIdx, List path) { this(myIdx, path); } /** * @return an empty canonical path to be extended */ public static Extender empty() { return new Extender(0, new ArrayList<>()); } /** * @return a new path builder */ public static Builder of() { return new Builder(new ArrayList<>()); } /** * Creates a new path instance from the "serialized" slash-separated representation. * *

The escape character is {@code '\'} and special characters are {@code '\'}, {@code '|'} and {@code '/'}. * * @param path the string representation of the path * @return a new path instance */ public static CanonicalPath fromString(String path) { return fromPartiallyUntypedString(path, new StructuredDataHintingTypeProvider()); } /** * @param path the canonical path to parse * @param typeProvider the type provider used to figure out types of segments that don't explicitly mention it * @return the parsed canonical path * @see Path#fromPartiallyUntypedString(String, TypeProvider) */ public static CanonicalPath fromPartiallyUntypedString(String path, TypeProvider typeProvider) { return (CanonicalPath) Path.fromString(path, true, Extender::new, new CanonicalTypeProvider(typeProvider)); } /** * An overload of {@link #fromPartiallyUntypedString(String, TypeProvider)} which uses the provided initial position * to figure out the possible type if is missing in the provided relative path. * * @param path the relative path to parse * @param initialPosition the initial position using which the types will be deduced for the segments that don't * specify the type explicitly * @param intendedFinalType the type of the final segment in the path. This can resolve potentially ambiguous * situations where, given the initial position, more choices are possible. * @return the parsed relative path */ public static CanonicalPath fromPartiallyUntypedString(String path, CanonicalPath initialPosition, Class intendedFinalType) { return fromPartiallyUntypedString(path, initialPosition, SegmentType.fromElementType(intendedFinalType)); } public static CanonicalPath fromPartiallyUntypedString(String path, CanonicalPath initialPosition, SegmentType intendedFinalType) { ExtenderConstructor ctor = (idx, list) -> { if (initialPosition != null) { list.addAll(initialPosition.getPath()); } return new Extender(idx, list); }; return (CanonicalPath) Path.fromString(path, true, ctor, new CanonicalTypeProvider( new HintedTypeProvider(intendedFinalType, new Extender(0, initialPosition == null ? new ArrayList<>() : new ArrayList<>(initialPosition.getPath())))) ); } public R accept(ElementTypeVisitor visitor, P parameter) { return getSegment().accept(visitor, parameter); } @Override public CanonicalPath toCanonicalPath() { return this; } @Override public RelativePath toRelativePath() { return RelativePath.empty().extend(getPath()).get(); } @SuppressWarnings("unchecked") @Override public Iterator ascendingIterator() { return (Iterator) super.ascendingIterator(); } @SuppressWarnings("unchecked") @Override public Iterator descendingIterator() { return (Iterator) super.descendingIterator(); } @Override public CanonicalPath down() { return (CanonicalPath) super.down(); } @Override public CanonicalPath down(int distance) { return (CanonicalPath) super.down(distance); } @Override protected CanonicalPath newInstance(int startIdx, int endIdx, List segments) { return new CanonicalPath(startIdx, endIdx, segments); } @Override public CanonicalPath up() { return (CanonicalPath) super.up(); } @Override public CanonicalPath up(int distance) { return (CanonicalPath) super.up(distance); } /** * @return The path to the root resource as known to this path instance. */ @ApiModelProperty(hidden = true) public CanonicalPath getRoot() { return new CanonicalPath(1, path); } /** * If this path was created by going {@link #up() up} from another path, then this returns the bottom-most path * in such chain. * * @return the bottom-most path in the shared chain */ @ApiModelProperty(hidden = true) public CanonicalPath getLeaf() { return new CanonicalPath(path.size(), path); } public boolean isParentOf(CanonicalPath other) { return super.isParentOf(other); } public RelativePath relativeTo(CanonicalPath root) { if (root == null) { throw new IllegalArgumentException("root == null"); } //establish the common parts of root and this. int maxCommonPrefixLength = Math.min(this.endIdx, root.endIdx); int commonPrefixLength = 0; for (; commonPrefixLength < maxCommonPrefixLength; ++commonPrefixLength) { if (!this.path.get(commonPrefixLength).equals(root.path.get(commonPrefixLength))) { break; } } RelativePath.Extender ret = RelativePath.empty(); for (int ups = root.endIdx; ups > commonPrefixLength; --ups) { ret = ret.extendUp(); } for (int downs = commonPrefixLength; downs < this.endIdx; downs++) { ret.extend(this.path.get(downs)); } return ret.get(); } /** * Creates a new path by appending the provided segment to the current path. The returned path does NOT share * the chain with the current path anymore. * *

The returned path will be the leaf path of the new path chain created by obtaining the segments of the * current path using {@link #getPath()} and appending the new segment to it. * *

It is checked that the new path is a valid canonical path. I.e. you cannot add a tenant segment under * a resource, etc. * * @param type the type of the entity to append * @param id the id of the appended entity * @return a new path instance * @throws IllegalArgumentException if adding the provided segment would create an invalid canonical path */ public Extender extend(SegmentType type, String id) { return modified().extend(new Segment(type, id)); } /** * The returned extender will produce a modified version of this path. This path will remain unaffected. * * @return an extender initialized with the current path */ @Override public Extender modified() { return new Extender(startIdx, new ArrayList<>(path.subList(0, endIdx))); } /** * The path contains ids of different entities. I.e. a path to environment always contains the id of the tenant * that the environment belongs to. * *

Using the returned extractor, one can obtain the ids of such different entities easily. * * @return the extractor object to use for extracting ids of different entity types from the path. */ public IdExtractor ids() { return new IdExtractor(); } @Override public String toString() { return new Encoder(x -> true).encode(Character.toString(PATH_DELIM), this); } @Override public Iterator iterator() { return ascendingIterator(); } public static final class Builder extends Path.Builder { private Builder(List list) { super(list, CanonicalPath::new); } @Override protected RelationshipBuilder relationshipBuilder(List list) { return new RelationshipBuilder(list); } @Override protected TenantBuilder tenantBuilder(List list) { return new TenantBuilder(list); } } public static final class TenantBuilder extends Path.TenantBuilder { private TenantBuilder(List list) { super(list, CanonicalPath::new); } @Override protected EnvironmentBuilder environmentBuilder(List list) { return new EnvironmentBuilder(list); } @Override protected FeedBuilder feedBuilder(List segments) { return new FeedBuilder(segments); } @Override protected ResourceTypeBuilder resourceTypeBuilder(List list) { return new ResourceTypeBuilder(list); } @Override protected MetricTypeBuilder metricTypeBuilder(List list) { return new MetricTypeBuilder(list); } @Override protected MetadataPackBuilder metadataPackBuilder(List segments) { return new MetadataPackBuilder(segments); } } public static final class MetadataPackBuilder extends Path.MetadataPackBuilder { private MetadataPackBuilder(List segments) { super(segments, CanonicalPath::new); } } public static final class EnvironmentBuilder extends Path.EnvironmentBuilder { private EnvironmentBuilder(List list) { super(list, CanonicalPath::new); } @Override protected ResourceBuilder resourceBuilder(List segments) { return new ResourceBuilder(segments); } @Override protected MetricBuilder metricBuilder(List segments) { return new MetricBuilder(segments); } } public static final class ResourceTypeBuilder extends Path.ResourceTypeBuilder { private ResourceTypeBuilder(List list) { super(list, CanonicalPath::new); } @Override protected OperationTypeBuilder operationTypeBuilder(List segments) { return new OperationTypeBuilder(segments); } @Override protected StructuredDataBuilder structuredDataBuilder(List segments) { return new StructuredDataBuilder(segments); } } public static final class MetricTypeBuilder extends Path.MetricTypeBuilder { private MetricTypeBuilder(List segments) { super(segments, CanonicalPath::new); } } public static final class OperationTypeBuilder extends Path.OperationTypeBuilder { private OperationTypeBuilder(List segments) { super(segments, CanonicalPath::new); } @Override protected StructuredDataBuilder structuredDataBuilder(List segments) { return new StructuredDataBuilder(segments); } } public static final class ResourceBuilder extends Path.ResourceBuilder { private ResourceBuilder(List segments) { super(segments, CanonicalPath::new); } @Override protected MetricBuilder metricBuilder(List segments) { return new MetricBuilder(segments); } @Override protected StructuredDataBuilder structuredDataBuilder(List segments) { return new StructuredDataBuilder(segments); } } public static final class MetricBuilder extends Path.MetricBuilder { private MetricBuilder(List segments) { super(segments, CanonicalPath::new); } } public static final class FeedBuilder extends Path.FeedBuilder { private FeedBuilder(List list) { super(list, CanonicalPath::new); } @Override protected ResourceTypeBuilder resourceTypeBuilder(List segments) { return new ResourceTypeBuilder(segments); } @Override protected MetricTypeBuilder metricTypeBuilder(List segments) { return new MetricTypeBuilder(segments); } @Override protected ResourceBuilder resourceBuilder(List segments) { return new ResourceBuilder(segments); } @Override protected MetricBuilder metricBuilder(List segments) { return new MetricBuilder(segments); } } public static final class RelationshipBuilder extends Path.RelationshipBuilder { private RelationshipBuilder(List segments) { super(segments, CanonicalPath::new); } } public static final class StructuredDataBuilder extends Path.StructuredDataBuilder { private StructuredDataBuilder(List segments) { super(segments, CanonicalPath::new); } } public final class IdExtractor { public String getTenantId() { return idIfTypeCorrect(getRoot(), SegmentType.t); } public String getEnvironmentId() { return idIfTypeCorrect(getRoot().down(), SegmentType.e); } public String getMetricTypeId() { if (getFeedId() != null) { return idIfTypeCorrect(getRoot().down(2), SegmentType.mt); } else { return idIfTypeCorrect(getRoot().down(), SegmentType.mt); } } public String getResourceTypeId() { if (getFeedId() != null) { return idIfTypeCorrect(getRoot().down(2), SegmentType.rt); } else { return idIfTypeCorrect(getRoot().down(), SegmentType.rt); } } public String getFeedId() { return idIfTypeCorrect(getRoot().down(), SegmentType.f); } /** * This creates a relative path that represents the resource hierarchy present in this path. *

* Note that the returned relative path is backed by this canonical path and therefore you can call * {@link RelativePath#slide(int, int)} on it and modify it to "see" the parts of the path "above" and "below" * the resource segments. * * @return the resource hierarchy in the path represented as a relative path or null if there are no resources * in the path. */ public RelativePath getResourcePath() { int from, to; List path = CanonicalPath.this.path; for (from = CanonicalPath.this.startIdx; from < CanonicalPath.this.endIdx; ++from) { if (SegmentType.r.equals(path.get(from).getElementType())) { break; } } if (from == path.size()) { return null; } for (to = from; to < CanonicalPath.this.endIdx; ++to) { if (!SegmentType.r.equals(path.get(to).getElementType())) { break; } } return new RelativePath(from, to, CanonicalPath.this.path); } public String getMetricId() { return idIfTypeCorrect(CanonicalPath.this, SegmentType.m); } public String getRelationshipId() { return idIfTypeCorrect(getRoot(), SegmentType.rl); } public String getOperationTypeId() { if (getFeedId() != null) { return idIfTypeCorrect(getRoot().down(3), SegmentType.ot); } else { return idIfTypeCorrect(getRoot().down(2), SegmentType.ot); } } public String getDataRole() { CanonicalPath currentPath = CanonicalPath.this; //move up from the potential data path segments while (SegmentType.sd.equals(currentPath.getSegment().getElementType())) { currentPath = currentPath.up(); } //now we should be at the data entity, which should contain our role String roleStr = idIfTypeCorrect(currentPath, SegmentType.d); return roleStr; } private String idIfTypeCorrect(CanonicalPath path, SegmentType desiredType) { if (path.isDefined() && path.getSegment().getElementType().equals(desiredType)) { return path.getSegment().getElementId(); } else { return null; } } } public static class Extender extends Path.Extender { Extender(int from, List segments) { super(from, segments, true, (s) -> s.isEmpty() ? Arrays.asList(SegmentType.t, SegmentType.rl) : VALID_PROGRESSIONS.get(s.get(s.size() - 1).getElementType())); } @Override protected CanonicalPath newPath(int startIdx, int endIdx, List segments) { return new CanonicalPath(startIdx, endIdx, segments); } @Override public Extender extend(Segment segment) { return (Extender) super.extend(segment); } public Extender extend(Collection segments) { return (Extender) super.extend(segments); } @Override public Extender extend(SegmentType type, String id) { return (Extender) super.extend(type, id); } @Override public CanonicalPath get() { return (CanonicalPath) super.get(); } } private static class CanonicalTypeProvider extends EnhancedTypeProvider { private final TypeProvider wrapped; private CanonicalTypeProvider(TypeProvider wrapped) { this.wrapped = wrapped; } @Override public void segmentParsed(Segment segment) { if (wrapped != null) { wrapped.segmentParsed(segment); } } @Override public Segment deduceSegment(String type, String id, boolean isLast) { if (type != null && !type.isEmpty()) { if (id == null || id.isEmpty()) { return null; } else { return new Segment(SegmentType.fastValueOf(type), id); } } if (id == null || id.isEmpty()) { return null; } SegmentType cls = SegmentType.fastValueOf(id); if (cls == null && wrapped != null) { return wrapped.deduceSegment(type, id, isLast); } else if (cls != null) { return new Segment(cls, id); } return null; } @Override public void finished() { if (wrapped != null) { wrapped.finished(); } } @Override Set getValidTypeName() { return SegmentType.getCanonicalShortNames(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy