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

org.cometd.bayeux.ChannelId Maven / Gradle / Ivy

/*
 * Copyright (c) 2010 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.cometd.bayeux;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 

Reification of a {@link Channel#getId() channel id} with methods to test properties * and compare with other {@link ChannelId}s.

*

A {@link ChannelId} breaks the channel id into path segments so that, for example, * {@code /foo/bar} breaks into {@code ["foo","bar"]}.

*

{@link ChannelId} can be wild, when they end with one or two wild characters {@code "*"}; * a {@link ChannelId} is shallow wild if it ends with one wild character (for example {@code /foo/bar/*}) * and deep wild if it ends with two wild characters (for example {@code /foo/bar/**}).

*/ public class ChannelId { public final static String WILD = "*"; public final static String DEEPWILD = "**"; private final String _id; private volatile String[] _segments; private int _wild; private List _wilds; private String _parent; /** * Constructs a new {@code ChannelId} with the given id * * @param id the channel id in string form */ public ChannelId(String id) { if (id == null || id.length() == 0 || id.charAt(0) != '/' || "/".equals(id)) throw new IllegalArgumentException("Invalid channel id: " + id); id = id.trim(); if (id.charAt(id.length() - 1) == '/') id = id.substring(0, id.length() - 1); _id = id; } private void resolve() { synchronized (this) { if (_segments != null) return; String[] segments = _id.substring(1).split("/"); if (segments.length < 1) throw new IllegalArgumentException("Invalid channel id:" + this); String lastSegment = segments[segments.length - 1]; int wild = 0; if (WILD.equals(lastSegment)) wild = 1; else if (DEEPWILD.equals(lastSegment)) wild = 2; _wild = wild; if (wild > 0) { _wilds = Collections.emptyList(); } else { String[] wilds = new String[segments.length + 1]; StringBuilder b = new StringBuilder(_id.length()); b.append('/'); for (int i = 0; i < segments.length; ++i) { if (segments[i].trim().length() == 0) throw new IllegalArgumentException("Invalid channel id:" + this); if (i > 0) b.append(segments[i - 1]).append('/'); wilds[segments.length - i] = b + "**"; } wilds[0] = b + "*"; _wilds = Collections.unmodifiableList(Arrays.asList(wilds)); } _parent = segments.length == 1 ? null : _id.substring(0, _id.length() - lastSegment.length() - 1); // Volatile write, other members will be visible as well _segments = segments; } } /** * @return whether this {@code ChannelId} is either {@link #isShallowWild() shallow wild} * or {@link #isDeepWild() deep wild} */ public boolean isWild() { resolve(); return _wild > 0; } /** *

Shallow wild {@code ChannelId}s end with a single wild character {@code "*"} * and {@link #matches(ChannelId) match} non wild channels with * the same {@link #depth() depth}.

*

Example: {@code /foo/*} matches {@code /foo/bar}, but not {@code /foo/bar/baz}.

* * @return whether this {@code ChannelId} is a shallow wild channel id */ public boolean isShallowWild() { return isWild() && !isDeepWild(); } /** *

Deep wild {@code ChannelId}s end with a double wild character "**" * and {@link #matches(ChannelId) match} non wild channels with * the same or greater {@link #depth() depth}.

*

Example: {@code /foo/**} matches {@code /foo/bar} and {@code /foo/bar/baz}.

* * @return whether this {@code ChannelId} is a deep wild channel id */ public boolean isDeepWild() { resolve(); return _wild > 1; } /** *

A {@code ChannelId} is a meta {@code ChannelId} if it starts with {@code "/meta/"}.

* * @return whether the first segment is "meta" */ public boolean isMeta() { return isMeta(_id); } /** *

A {@code ChannelId} is a service {@code ChannelId} if it starts with {@code "/service/"}.

* * @return whether the first segment is "service" */ public boolean isService() { return isService(_id); } /** * @return whether this {@code ChannelId} is neither {@link #isMeta() meta} nor {@link #isService() service} */ public boolean isBroadcast() { return isBroadcast(_id); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof ChannelId)) return false; ChannelId that = (ChannelId)obj; return _id.equals(that._id); } @Override public int hashCode() { return _id.hashCode(); } /** *

Tests whether this {@code ChannelId} matches the given {@code ChannelId}.

*

If the given {@code ChannelId} is {@link #isWild() wild}, * then it matches only if it is equal to this {@code ChannelId}.

*

If this {@code ChannelId} is non-wild, * then it matches only if it is equal to the given {@code ChannelId}.

*

Otherwise, this {@code ChannelId} is either shallow or deep wild, and * matches {@code ChannelId}s with the same number of equal segments (if it is * shallow wild), or {@code ChannelId}s with the same or a greater number of * equal segments (if it is deep wild).

* * @param channelId the channelId to match * @return true if this {@code ChannelId} matches the given {@code ChannelId} */ public boolean matches(ChannelId channelId) { resolve(); if (channelId.isWild()) return equals(channelId); switch (_wild) { case 0: { return equals(channelId); } case 1: { if (channelId._segments.length != _segments.length) return false; for (int i = _segments.length - 1; i-- > 0; ) if (!_segments[i].equals(channelId._segments[i])) return false; return true; } case 2: { if (channelId._segments.length < _segments.length) return false; for (int i = _segments.length - 1; i-- > 0; ) if (!_segments[i].equals(channelId._segments[i])) return false; return true; } default: { throw new IllegalStateException(); } } } @Override public String toString() { return _id; } /** * @return how many segments this {@code ChannelId} is made of * @see #getSegment(int) */ public int depth() { resolve(); return _segments.length; } /** * @param id the channel to test * @return whether this {@code ChannelId} is an ancestor of the given {@code ChannelId} * @see #isParentOf(ChannelId) */ public boolean isAncestorOf(ChannelId id) { resolve(); if (isWild() || depth() >= id.depth()) return false; for (int i = _segments.length; i-- > 0; ) { if (!_segments[i].equals(id._segments[i])) return false; } return true; } /** * @param id the channel to test * @return whether this {@code ChannelId} is the parent of the given {@code ChannelId} * @see #isAncestorOf(ChannelId) */ public boolean isParentOf(ChannelId id) { resolve(); if (isWild() || depth() != id.depth() - 1) return false; for (int i = _segments.length; i-- > 0; ) { if (!_segments[i].equals(id._segments[i])) return false; } return true; } /** * @return the {@code ChannelId} parent of this {@code ChannelId} * @see #isParentOf(ChannelId) */ public String getParent() { resolve(); return _parent; } /** * @param i the segment index * @return the i-nth segment of this channel, or null if no such segment exist * @see #depth() */ public String getSegment(int i) { resolve(); if (i >= _segments.length) return null; return _segments[i]; } /** * @return The list of wilds channels that match this channel, or * the empty list if this channel is already wild. */ public List getWilds() { resolve(); return _wilds; } /** *

Helper method to test if the string form of a {@code ChannelId} * represents a {@link #isMeta() meta} {@code ChannelId}.

* * @param channelId the channel id to test * @return whether the given channel id is a meta channel id */ public static boolean isMeta(String channelId) { return channelId != null && channelId.startsWith("/meta/"); } /** *

Helper method to test if the string form of a {@code ChannelId} * represents a {@link #isService()} service} {@code ChannelId}.

* * @param channelId the channel id to test * @return whether the given channel id is a service channel id */ public static boolean isService(String channelId) { return channelId != null && channelId.startsWith("/service/"); } /** *

Helper method to test if the string form of a {@code ChannelId} * represents a {@link #isBroadcast()} broadcast} {@code ChannelId}.

* * @param channelId the channel id to test * @return whether the given channel id is a broadcast channel id */ public static boolean isBroadcast(String channelId) { return !isMeta(channelId) && !isService(channelId); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy