/*
* Copyright 2016, Red Hat Inc. and/or its affiliates.
*
* 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.infinispan.objectfilter.impl.ql;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A property path (e.g. {@code foo.bar.baz}) represented by a {@link List} of {@link PropertyReference}s, used
* in a SELECT, GROUP BY, ORDER BY, WHERE or HAVING clause.
*
* @author Gunnar Morling
* @author anistor@redhat.com
* @since 9.0
*/
public class PropertyPath<TypeDescriptor> {
public static final class PropertyReference<TypeDescriptor> {
private final String propertyName;
private final TypeDescriptor typeDescriptor;
private final boolean isAlias;
public PropertyReference(String propertyName, TypeDescriptor typeDescriptor, boolean isAlias) {
this.propertyName = propertyName;
this.typeDescriptor = typeDescriptor;
this.isAlias = isAlias;
}
public String getPropertyName() {
return propertyName;
}
public boolean isAlias() {
return isAlias;
}
public TypeDescriptor getTypeDescriptor() {
return typeDescriptor;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || o.getClass() != getClass()) return false;
PropertyReference<?> that = (PropertyReference<?>) o;
return isAlias == that.isAlias && propertyName.equals(that.propertyName)
&& (typeDescriptor != null ? typeDescriptor.equals(that.typeDescriptor) : that.typeDescriptor == null);
}
@Override
public int hashCode() {
int result = propertyName.hashCode();
result = 31 * result + (isAlias ? 1 : 0);
result = 31 * result + (typeDescriptor != null ? typeDescriptor.hashCode() : 0);
return result;
}
@Override
public String toString() {
return propertyName;
}
}
private final LinkedList<PropertyReference<TypeDescriptor>> nodes;
private List<PropertyReference<TypeDescriptor>> unmodifiableNodes;
private String asStringPath;
private String asStringPathWithoutAlias;
private String[] asArrayPath;
/**
* Creates an empty path.
*/
public PropertyPath() {
this.nodes = new LinkedList<>();
}
/**
* Creates an path with a given list of nodes.
*/
public PropertyPath(List<PropertyReference<TypeDescriptor>> nodes) {
this.nodes = new LinkedList<>(nodes);
}
public boolean isAlias() {
return getFirst().isAlias();
}
public PropertyReference<TypeDescriptor> getFirst() {
return nodes.getFirst();
}
public PropertyReference<TypeDescriptor> getLast() {
return nodes.getLast();
}
public List<PropertyReference<TypeDescriptor>> getNodes() {
if (unmodifiableNodes == null) {
unmodifiableNodes = Collections.unmodifiableList(nodes);
}
return unmodifiableNodes;
}
public void append(PropertyReference<TypeDescriptor> propertyReference) {
nodes.add(propertyReference);
asArrayPath = null;
asStringPath = null;
asStringPathWithoutAlias = null;
}
public boolean isEmpty() {
return nodes.isEmpty();
}
public int getLength() {
return nodes.size();
}
public String asStringPath() {
if (asStringPath == null) {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (PropertyReference node : nodes) {
if (isFirst) {
isFirst = false;
} else {
sb.append('.');
}
sb.append(node.getPropertyName());
}
asStringPath = sb.toString();
}
return asStringPath;
}
public String asStringPathWithoutAlias() {
if (asStringPathWithoutAlias == null) {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (PropertyReference node : nodes) {
if (!node.isAlias()) {
if (isFirst) {
isFirst = false;
} else {
sb.append('.');
}
sb.append(node.getPropertyName());
}
}
asStringPathWithoutAlias = sb.toString();
}
return asStringPathWithoutAlias;
}
public String[] asArrayPath() {
if (asArrayPath == null) {
String[] arrayPath = new String[nodes.size()];
int i = 0;
for (PropertyReference<?> pr : nodes) {
arrayPath[i++] = pr.getPropertyName();
}
asArrayPath = arrayPath;
}
return asArrayPath;
}
public List<String> getNodeNamesWithoutAlias() {
List<String> list = new ArrayList<>(nodes.size());
for (PropertyReference<TypeDescriptor> node : nodes) {
if (!node.isAlias()) {
list.add(node.getPropertyName());
}
}
return list;
}
public List<PropertyReference<TypeDescriptor>> getNodesWithoutAlias() {
List<PropertyReference<TypeDescriptor>> list = new ArrayList<>(nodes.size());
for (PropertyReference<TypeDescriptor> node : nodes) {
if (!node.isAlias()) {
list.add(node);
}
}
return list;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || o.getClass() != getClass()) return false;
PropertyPath<?> that = (PropertyPath<?>) o;
return nodes.equals(that.nodes);
}
@Override
public int hashCode() {
return nodes.hashCode();
}
@Override
public String toString() {
return asStringPath();
}
public static <TypeDescriptor> PropertyPath<TypeDescriptor> make(String propertyPath) {
String[] splinters = propertyPath.split("[.]");
List<PropertyReference<TypeDescriptor>> nodes = new ArrayList<>(splinters.length);
for (String name : splinters) {
nodes.add(new PropertyPath.PropertyReference<>(name, null, false));
}
return new PropertyPath<>(nodes);
}
}