001/*
002 * Copyright 2021 Global Biodiversity Information Facility (GBIF)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.gbif.dwc.terms;
017
018import java.io.Serializable;
019import java.io.UnsupportedEncodingException;
020import java.net.URI;
021import java.net.URLEncoder;
022import java.nio.charset.StandardCharsets;
023import java.util.Objects;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026
027public class UnknownTerm implements Term, Serializable {
028
029  private final URI uri;
030  private final String name;
031  private final String prefix;
032  private final boolean isClass;
033
034  private static final String NS = "unknown.org";
035  private static final String SCHEME = "http://";
036  private static final String URL = SCHEME + NS + "/";
037  private static final String PREFIX = "unknown";
038  private static final Pattern PREFIX_PATTERN = Pattern.compile("^([a-zA-Z0-9]+):([a-zA-Z0-9._+# -]+)$");
039
040  public static UnknownTerm build(String name){
041    return build(name, false);
042  }
043
044  private static URI unknownURI(String name) {
045    return URI.create(URL + name);
046  }
047
048  public static UnknownTerm build(String name, boolean isClass){
049    Matcher m = PREFIX_PATTERN.matcher(name);
050    if (m.find()) {
051      if (m.group(1).equalsIgnoreCase(PREFIX)) {
052        // unknown:
053        return new UnknownTerm(unknownURI(m.group(2)), PREFIX, m.group(2), isClass);
054      } else {
055        return new UnknownTerm(unknownURI(m.group(1) + "/" + m.group(2)), m.group(1), m.group(2), isClass);
056      }
057    }
058    URI uri = URI.create(name);
059    if (uri.getAuthority() != null) {
060      return new UnknownTerm(uri, isClass);
061
062    } else if (uri.getScheme() != null) {
063      String scheme = uri.getScheme().equalsIgnoreCase(PREFIX) ? "" : uri.getScheme() + "/";
064      return build(URL + scheme + uri.getSchemeSpecificPart(), isClass);
065
066    } else {
067      if (!name.contains("/") && !name.contains("#")) {
068        try {
069          name = URLEncoder.encode(name, StandardCharsets.UTF_8.name());
070        } catch (UnsupportedEncodingException e) {
071        }
072      }
073      return build(URL +name, isClass);
074    }
075  }
076
077  public static UnknownTerm build(String qualifiedName, String simpleName){
078    return build(qualifiedName, simpleName, false);
079  }
080
081  public static UnknownTerm build(String qualifiedName, String simpleName, boolean isClass){
082    return new UnknownTerm(URI.create(qualifiedName), null, simpleName, isClass);
083  }
084
085  public UnknownTerm(URI uri, String prefix, String name, boolean isClass) {
086    this.isClass = isClass;
087    if (uri == null || !uri.isAbsolute()) {
088      throw new IllegalArgumentException("The qualified name URI must be an absolute URI");
089    }
090    if (name == null || name.isEmpty()) {
091      throw new IllegalArgumentException("The simple name is required");
092    }
093    this.uri = uri;
094    this.name = name;
095    this.prefix = prefix != null ? prefix : (
096        uri.getAuthority().equalsIgnoreCase(NS) ? PREFIX : null
097    );
098  }
099
100  public UnknownTerm(URI uri, boolean isClass) {
101    this(uri, null, extractName(uri), isClass);
102  }
103
104  private static String extractName(URI uri) {
105    if (uri == null || !uri.isAbsolute()) {
106      throw new IllegalArgumentException("The qualified name URI is required and must be an absolute URI");
107    }
108
109    if (uri.getFragment() != null) {
110      return uri.getFragment();
111
112    } else if (uri.getPath() != null) {
113      String name = uri.getPath();
114      // remove trailing and ending slash if existing
115      if (name.endsWith("/")) {
116        name = name.substring(0, name.length() - 1);
117      }
118      if (name.startsWith("/")) {
119        name = name.substring(1);
120      }
121      // only use last part of path
122      int pos = name.lastIndexOf("/");
123      if (pos > 0) {
124        name = name.substring(pos + 1);
125      }
126      return name;
127
128    } else {
129      throw new IllegalArgumentException("The qualified name URI must have a path or fragment to automatically derive a simple name");
130    }
131  }
132
133  @Override
134  public String qualifiedName() {
135    return uri.toString();
136  }
137
138  @Override
139  public String simpleName() {
140    return name;
141  }
142
143  @Override
144  public boolean isClass() {
145    return isClass;
146  }
147
148  @Override
149  public String toString() {
150    return prefixedName();
151  }
152
153  @Override
154  public String prefixedName() {
155    return prefix != null ? prefix + ":" + simpleName() : qualifiedName();
156  }
157
158  @Override
159  public String prefix() {
160    return prefix;
161  }
162
163  @Override
164  public URI namespace() {
165    return URI.create(uri.getScheme() + "://" + uri.getAuthority());
166  }
167
168  @Override
169  public boolean equals(Object o) {
170    if (this == o) return true;
171    if (!(o instanceof Term)) return false;
172
173    Term that = (Term) o;
174
175    return qualifiedName().equals(that.qualifiedName());
176  }
177
178  @Override
179  public int hashCode() {
180    return qualifiedName().hashCode();
181  }
182}