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

org.hawkular.inventory.paths.RelativePath 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.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 java.util.function.Function;

import io.swagger.annotations.ApiModel;

/**
 * A relative path is used in the API to refer to other entities during association. Its precise meaning is
 * context-sensitive but the basic idea is that given a position in the graph, you want to refer to other entities that
 * are "near" without needing to provide their full canonical path.
 *
 * 

I.e. it is quite usual only associate resources and metrics from a single environment. It would be cumbersome to * require the full canonical path for every metric one wants to associate with a resource. Therefore a partial path is * used to refer to the metric. * *

The relative path contains one special segment type - encoded as ".." and represented using the * {@link org.hawkular.inventory.paths.RelativePath.Up} class that can be used to go up in the relative path. * * @author Lukas Krejci * @since 0.2.0 */ @ApiModel public final class RelativePath extends Path implements Serializable { static final Map> SHORT_NAME_TYPES = new HashMap<>(); private static final Map> VALID_PROGRESSIONS = new HashMap<>(); static { for (SegmentType c : SegmentType.values()) { EnumSet progressions = CanonicalPath.VALID_PROGRESSIONS.get(c); if (progressions == null) { progressions = EnumSet.of(SegmentType.up); } else { progressions = EnumSet.of(SegmentType.up, progressions.toArray(new SegmentType[progressions.size()])); } VALID_PROGRESSIONS.put(c, progressions); } } RelativePath(int start, int end, List segments) { super(start, end, segments); } public static RelativePath fromString(String path) { return fromPartiallyUntypedString(path, new StructuredDataHintingTypeProvider()); } /** * @param path the relative path to parse * @param typeProvider the type provider used to figure out types of segments that don't explicitly mention it * @return the parsed relative path * @see Path#fromPartiallyUntypedString(String, TypeProvider) */ public static RelativePath fromPartiallyUntypedString(String path, TypeProvider typeProvider) { return (RelativePath) Path.fromString(path, false, Extender::new, new RelativeTypeProvider(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 RelativePath fromPartiallyUntypedString(String path, CanonicalPath initialPosition, SegmentType intendedFinalType) { return (RelativePath) Path.fromString(path, false, Extender::new, new RelativeTypeProvider(new HintedTypeProvider(intendedFinalType, new RelativePath.Extender(0, new ArrayList<>(initialPosition.getPath()))))); } /** * @return an empty canonical path to be extended */ public static Extender empty() { return new Extender(0, new ArrayList<>()); } public static Builder to() { return new Builder(new ArrayList<>()); } @Override protected Path newInstance(int startIdx, int endIdx, List segments) { return new RelativePath(startIdx, endIdx, segments); } /** * Applies this relative path on the provided canonical path. * * @param path */ public CanonicalPath applyTo(CanonicalPath path) { return toCanonicalPath(new ArrayList<>(path.getPath())); } /** * Tries to convert this relative path to a canonical path. This will only succeed if this relative path truly * represents a canonical path and thus can be converted to it. * *

I.e. this will not work on relative paths like {@code ../r;id} which doesn't itself represent a full canonical * path. * * @return a canonical path constructed from this relative path * @throws IllegalArgumentException if the attempt to convert to canonical path fails */ public CanonicalPath toCanonicalPath() { return toCanonicalPath(new ArrayList<>()); } public RelativePath toRelativePath() { return this; } private CanonicalPath toCanonicalPath(List startSegments) { CanonicalPath.Extender extender = new CanonicalPath.Extender(0, startSegments) { @Override public CanonicalPath.Extender extend(Segment segment) { if (SegmentType.up.equals(segment.getElementType())) { removeLastSegment(); } else { super.extend(segment); } return this; } }; getPath().forEach(extender::extend); return extender.get(); } @Override public Extender modified() { return new Extender(startIdx, new ArrayList<>(path.subList(0, endIdx))); } @SuppressWarnings("unchecked") @Override public Iterator ascendingIterator() { return (Iterator) super.ascendingIterator(); } @SuppressWarnings("unchecked") @Override public Iterator descendingIterator() { return (Iterator) super.descendingIterator(); } @Override public RelativePath down() { return (RelativePath) super.down(); } @Override public RelativePath down(int distance) { return (RelativePath) super.down(distance); } @Override public RelativePath up() { return (RelativePath) super.up(); } @Override public RelativePath up(int distance) { return (RelativePath) super.up(distance); } /** * Moves the start and end of the path by the provided distances. *

* Consider the path: *

{@code a/b/c}
*

* {@code p1 = p.slide(1, 0)} will produce {@code p1 = "b/c"}, {@code p1.slide(-1, 0)} will produce a path * equivalent to the original one. {@code p.slide(-1, 0)} will produce an undefined path, because it would go past * the known start of the path (i.e. beyond "a"). * * @param startDelta the number of steps to move the start of the path * @param endDelta the number of steps to move the end of the path * @return a new relative path with modified length and position, possibly undefined */ public RelativePath slide(int startDelta, int endDelta) { return new RelativePath(startIdx + startDelta, endIdx + endDelta, path); } public boolean isParentOf(RelativePath other) { return super.isParentOf(other); } @Override public String toString() { return new Encoder((s) -> !SegmentType.up.equals(s.getElementType())).encode("", this); } public static final class Up { public static final SegmentType SEGMENT_TYPE = SegmentType.up; private Up() { } } public static final class Builder extends Path.Builder { private Builder(List list) { super(list, RelativePath::new); } @Override protected RelationshipBuilder relationshipBuilder(List list) { return new RelationshipBuilder(list); } @Override protected TenantBuilder tenantBuilder(List list) { return new TenantBuilder(list); } public EnvironmentBuilder environment(String id) { segments.add(new Segment(SegmentType.e, id)); return new EnvironmentBuilder(segments); } public ResourceTypeBuilder resourceType(String id) { segments.add(new Segment(SegmentType.rt, id)); return new ResourceTypeBuilder(segments); } public MetricTypeBuilder metricType(String id) { segments.add(new Segment(SegmentType.mt, id)); return new MetricTypeBuilder(segments); } public FeedBuilder feed(String id) { segments.add(new Segment(SegmentType.f, id)); return new FeedBuilder(segments); } public ResourceBuilder resource(String id) { segments.add(new Segment(SegmentType.r, id)); return new ResourceBuilder(segments); } public MetricBuilder metric(String id) { segments.add(new Segment(SegmentType.m, id)); return new MetricBuilder(segments); } public StructuredDataBuilder dataEntity(DataRole role) { segments.add(new Segment(SegmentType.d, role.name())); return new StructuredDataBuilder(segments); } public OperationTypeBuilder operationType(String id) { segments.add(new Segment(SegmentType.ot, id)); return new OperationTypeBuilder(segments); } public StructuredDataBuilder structuredData() { return new StructuredDataBuilder(segments); } public MetadataPackBuilder metadataPack() { return new MetadataPackBuilder(segments); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class TenantBuilder extends Path.TenantBuilder { private TenantBuilder(List list) { super(list, RelativePath::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 UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class EnvironmentBuilder extends Path.EnvironmentBuilder { private EnvironmentBuilder(List list) { super(list, RelativePath::new); } @Override protected ResourceBuilder resourceBuilder(List segments) { return new ResourceBuilder(segments); } @Override protected MetricBuilder metricBuilder(List segments) { return new MetricBuilder(segments); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class ResourceTypeBuilder extends Path.ResourceTypeBuilder { private ResourceTypeBuilder(List list) { super(list, RelativePath::new); } @Override protected OperationTypeBuilder operationTypeBuilder(List segments) { return new OperationTypeBuilder(segments); } @Override protected StructuredDataBuilder structuredDataBuilder(List segments) { return new StructuredDataBuilder(segments); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class MetricTypeBuilder extends Path.MetricTypeBuilder { private MetricTypeBuilder(List segments) { super(segments, RelativePath::new); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class OperationTypeBuilder extends Path.OperationTypeBuilder { private OperationTypeBuilder(List segments) { super(segments, RelativePath::new); } @Override protected StructuredDataBuilder structuredDataBuilder(List segments) { return new StructuredDataBuilder(segments); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class ResourceBuilder extends Path.ResourceBuilder { private ResourceBuilder(List segments) { super(segments, RelativePath::new); } @Override protected MetricBuilder metricBuilder(List segments) { return new MetricBuilder(segments); } @Override protected StructuredDataBuilder structuredDataBuilder(List segments) { return new StructuredDataBuilder(segments); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class MetricBuilder extends Path.MetricBuilder { private MetricBuilder(List segments) { super(segments, RelativePath::new); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class FeedBuilder extends Path.FeedBuilder { private FeedBuilder(List list) { super(list, RelativePath::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 UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class RelationshipBuilder extends Path.RelationshipBuilder { private RelationshipBuilder(List segments) { super(segments, RelativePath::new); } } public static final class StructuredDataBuilder extends Path.StructuredDataBuilder { private StructuredDataBuilder(List segments) { super(segments, RelativePath::new); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return new UpBuilder(segments); } } public static final class MetadataPackBuilder extends Path.MetadataPackBuilder { private MetadataPackBuilder(List segments) { super(segments, RelativePath::new); } } public static class UpBuilder extends AbstractBuilder { UpBuilder(List segments) { super(segments, RelativePath::new); } public TenantBuilder tenant(String id) { segments.add(new Segment(SegmentType.t, id)); return new TenantBuilder(segments); } public EnvironmentBuilder environment(String id) { segments.add(new Segment(SegmentType.e, id)); return new EnvironmentBuilder(segments); } public ResourceTypeBuilder resourceType(String id) { segments.add(new Segment(SegmentType.rt, id)); return new ResourceTypeBuilder(segments); } public MetricTypeBuilder metricType(String id) { segments.add(new Segment(SegmentType.mt, id)); return new MetricTypeBuilder(segments); } public FeedBuilder feed(String id) { segments.add(new Segment(SegmentType.f, id)); return new FeedBuilder(segments); } public ResourceBuilder resource(String id) { segments.add(new Segment(SegmentType.r, id)); return new ResourceBuilder(segments); } public MetricBuilder metric(String id) { segments.add(new Segment(SegmentType.m, id)); return new MetricBuilder(segments); } public StructuredDataBuilder dataEntity(String role) { segments.add(new Segment(SegmentType.d, role)); return new StructuredDataBuilder(segments); } public OperationTypeBuilder operationType(String id) { segments.add(new Segment(SegmentType.ot, id)); return new OperationTypeBuilder(segments); } public StructuredDataBuilder structuredData() { return new StructuredDataBuilder(segments); } public UpBuilder up() { segments.add(new Segment(SegmentType.up, null)); return this; } @Override public RelativePath get() { return super.get(); } } public static class Extender extends Path.Extender { Extender(int from, List segments) { this(from, segments, (segs) -> { if (segs.isEmpty()) { return SegmentType.getRelativeShortNames(); } SegmentType lastType = segs.get(segs.size() - 1).getElementType(); int idx = segs.size() - 2; int jump = 1; while (SegmentType.up.equals(lastType)) { while (idx >= 0 && SegmentType.up.equals(segs.get(idx).getElementType())) { idx--; jump++; } idx -= jump; if (idx < 0) { return SegmentType.getRelativeShortNames(); } else if (idx >= 0) { lastType = segs.get(idx).getElementType(); } } return VALID_PROGRESSIONS.get(lastType); }); } Extender(int from, List segments, Function, Collection> validProgressions) { super(from, segments, false, validProgressions); } @Override protected RelativePath newPath(int startIdx, int endIdx, List segments) { return new RelativePath(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); } public Extender extendUp() { return (Extender) super.extend(new Segment(SegmentType.up, null)); } @Override public RelativePath get() { return (RelativePath) super.get(); } } private static class RelativeTypeProvider extends EnhancedTypeProvider { private final TypeProvider wrapped; private RelativeTypeProvider(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()) { SegmentType cls = SegmentType.fastValueOf(type); if (!SegmentType.up.equals(cls) && (id == null || id.isEmpty())) { return null; } else if (id == null || id.isEmpty()) { return new Segment(cls, null); //cls == up } else if (SegmentType.up.equals(cls)) { throw new IllegalArgumentException("The \"up\" path segment cannot have an id."); } else { return new Segment(cls, 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 (SegmentType.up.equals(cls)) { return new Segment(cls, null); } else { return null; } } @Override public void finished() { if (wrapped != null) { wrapped.finished(); } } @Override Set getValidTypeName() { return SHORT_NAME_TYPES.keySet(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy