annis.service.objects.Match Maven / Gradle / Ivy
/*
* Copyright 2009-2011 Collaborative Research Centre SFB 632
*
* 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 annis.service.objects;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessOrder;
import javax.xml.bind.annotation.XmlAccessorOrder;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;
/**
* Represents a single match of an AQL query.
*
* @author Thomas Krause {@literal }
*/
@XmlRootElement
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class Match implements Serializable {
private final static Logger log = LoggerFactory.getLogger(Match.class);
private final static Splitter matchSplitter = Splitter.on(" ").trimResults().omitEmptyStrings();
private final static Splitter annoIDSplitter = Splitter.on("::").trimResults().limit(3);
private static final Escaper spaceEscaper = Escapers.builder().addEscape(' ', "%20").addEscape('%', "%25").build();
private List saltIDs;
private List annos;
public Match() {
saltIDs = new ArrayList<>();
annos = new ArrayList<>();
}
public Match(Collection originalIDs) {
saltIDs = new ArrayList<>(originalIDs);
annos = new ArrayList<>(saltIDs.size());
for (int i = 0; i < saltIDs.size(); i++) {
annos.add("");
}
}
public Match(Collection originalIDs, Collection originalAnnos) {
saltIDs = new ArrayList<>(originalIDs);
annos = new ArrayList<>(originalAnnos);
}
public void addSaltId(URI id) {
addSaltId(id, null);
}
public void addSaltId(URI id, String anno) {
if (id != null) {
saltIDs.add(id);
if (anno == null) {
annos.add("");
} else {
annos.add(anno);
}
}
}
/**
* Get Salt IDs of the nodes that are part of the match.
*
* @return a list of IDs as URI
*/
@XmlElement(name = "id")
public List getSaltIDs() {
return saltIDs;
}
/**
* @see #getSaltIDs()
* @param saltIDs
* the list of IDs as URI
*/
public void setSaltIDs(List saltIDs) {
this.saltIDs = saltIDs;
}
/**
* Get the fully qualified annotation matched annotation names. This list must
* be the same size as {@link #getSaltIDs() }. If no annotation is matched, the
* list contains an entry with an empty string.
*
* @return list of annotation names
*/
@XmlElement(name = "anno")
public List getAnnos() {
if (annos == null || annos.size() != saltIDs.size()) {
createEmptyAnnoList();
}
return annos;
}
public void setAnnos(List annos) {
this.annos = annos;
}
private void createEmptyAnnoList() {
if (saltIDs != null) {
annos = new ArrayList<>(saltIDs.size());
for (int i = 0; i < saltIDs.size(); i++) {
annos.add("");
}
}
}
public static Match parseFromString(String raw) {
return parseFromString(raw, ' ');
}
public static Match parseFromString(String raw, char separator) {
Match match = new Match();
Splitter splitter = matchSplitter;
if (separator != ' ') {
splitter = Splitter.on(separator).trimResults().omitEmptyStrings();
}
for (String singleMatch : splitter.split(raw)) {
URI uri;
String id = "";
String anno = null;
if (singleMatch.startsWith("salt:/")) {
id = singleMatch;
} else {
// split into the annotation namespace/name and the salt URI
List components = annoIDSplitter.splitToList(singleMatch);
int componentsSize = components.size();
Preconditions.checkArgument(componentsSize == 3 || componentsSize == 2,
"A match containing " + "annotation information always has to have the form "
+ "ns::name::salt:/.... or name::salt:/....");
String ns = "";
String name = "";
if (componentsSize == 3) {
id = components.get(2);
ns = components.get(0);
name = components.get(1);
} else if (componentsSize == 2) {
id = components.get(1);
name = components.get(0);
}
if (ns.isEmpty()) {
anno = name;
} else {
anno = ns + "::" + name;
}
// undo any escaping for the annotation part
anno = anno.replace("%20", " ").replace("%25", "%").replace("%2C", ",");
}
try {
uri = new URI(id).normalize();
if (!"salt".equals(uri.getScheme())) {
throw new URISyntaxException("not a Salt id", uri.toString());
}
// check if the path ends with "/" (which was wrongly used by older ANNIS
// versions)
String path = uri.getPath();
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
// recreate the URI with the decoded path (returned by getPath())
uri = new URI(uri.getScheme(), uri.getHost(), path, uri.getFragment());
} catch (URISyntaxException ex) {
log.error("Invalid syntax for ID " + singleMatch, ex);
continue;
}
match.addSaltId(uri, anno);
}
return match;
}
/**
* Returns a space separated list of all Salt IDs.
*
* @return list of IDs as string
*/
@Override
public String toString() {
if (saltIDs != null && annos != null) {
Iterator itID = saltIDs.iterator();
Iterator itAnno = annos.iterator();
LinkedList asString = new LinkedList<>();
while (itID.hasNext() && itAnno.hasNext()) {
URI u = itID.next();
String anno = itAnno.next();
if (u != null) {
asString.add(singleMatchToString(u, anno));
}
}
return Joiner.on(" ").join(asString);
}
return "";
}
public static String singleMatchToString(URI uri, String anno) {
if (uri != null) {
String v = uri.toASCIIString();
if (anno != null && !anno.isEmpty()) {
v = spaceEscaper.escape(anno) + "::" + uri;
}
return v;
}
return "";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((annos == null) ? 0 : annos.hashCode());
result = prime * result + ((saltIDs == null) ? 0 : saltIDs.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Match other = (Match) obj;
if (annos == null) {
if (other.annos != null)
return false;
} else if (!annos.equals(other.annos))
return false;
if (saltIDs == null) {
if (other.saltIDs != null)
return false;
} else if (!saltIDs.equals(other.saltIDs))
return false;
return true;
}
}