/*
* Copyright 2015 MovingBlocks
*
* 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.terasology.assets;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.terasology.assets.exceptions.InvalidUrnException;
import org.terasology.module.sandbox.API;
import org.terasology.naming.Name;
import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A ResourceUrn is a urn of the structure "{moduleName}:{resourceName}[#{fragmentName}][!instance]".
* <ul>
* <li>moduleName is the name of the module containing or owning the resource</li>
* <li>resourceName is the name of the resource</li>
* <li>fragmentName is an optional identifier for a sub-part of the resource</li>
* <li>an instance urn indicates a resource that is am independant copy of a resource identified by the rest of the urn</li>
* </ul>
* ResourceUrn is immutable and comparable.
*
* @author Immortius
*/
@API
public final class ResourceUrn implements Comparable<ResourceUrn> {
public static final String RESOURCE_SEPARATOR = ":";
public static final String FRAGMENT_SEPARATOR = "#";
public static final String INSTANCE_INDICATOR = "!instance";
private static final Pattern URN_PATTERN = Pattern.compile("([^:]+):([^#!]+)(?:#([^!]+))?(!instance)?");
private final Name moduleName;
private final Name resourceName;
private final Name fragmentName;
private final boolean instance;
/**
* Creates a urn with the module and resource name from the provided urn, but the fragment name provided. This urn will not be an instance urn.
* @param urn The urn to create this urn from
* @param fragmentName The fragment name this urn should have
*/
public ResourceUrn(ResourceUrn urn, String fragmentName) {
this(urn, new Name(fragmentName), false);
}
/**
* Creates a urn with the module and resource name from the provided urn, but the fragment name provided. This urn will not be an instance urn.
* @param urn The urn to create this urn from
* @param fragmentName The fragment name this urn should have
*/
public ResourceUrn(ResourceUrn urn, Name fragmentName) {
this(urn, fragmentName, false);
}
/**
* Creates a urn with the module and resource name from the provided urn, but the fragment name provided.
* @param urn The urn to create this urn from
* @param fragmentName The fragment name this urn should have
* @param instance Whether this urn should be a fragment
*/
public ResourceUrn(ResourceUrn urn, String fragmentName, boolean instance) {
this(urn, new Name(fragmentName), instance);
}
/**
* Creates a urn with the module and resource name from the provided urn, but the fragment name provided.
* @param urn The urn to create this urn from
* @param fragmentName The fragment name this urn should have
* @param instance Whether this urn should be a fragment
*/
public ResourceUrn(ResourceUrn urn, Name fragmentName, boolean instance) {
this.moduleName = urn.getModuleName();
this.resourceName = urn.getResourceName();
this.fragmentName = fragmentName;
this.instance = instance;
}
/**
* Creates a ModuleUri with the given module:resource combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
*/
public ResourceUrn(String moduleName, String resourceName) {
this(new Name(moduleName), new Name(resourceName), false);
}
/**
* Creates a ModuleUri for an instance with a given module:resource(!instance) combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
* @param instance Whether this urn identifies an instance
*/
public ResourceUrn(String moduleName, String resourceName, boolean instance) {
this(new Name(moduleName), new Name(resourceName), Name.EMPTY, instance);
}
/**
* Creates a ModuleUri with the given module:resource combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
*/
public ResourceUrn(Name moduleName, Name resourceName) {
this(moduleName, resourceName, Name.EMPTY, false);
}
/**
* Creates a ModuleUri with the given module:resource(!instance) combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
* @param instance Whether this urn identifies an instance
*/
public ResourceUrn(Name moduleName, Name resourceName, boolean instance) {
this(moduleName, resourceName, Name.EMPTY, instance);
}
/**
* Creates a ModuleUri with the given module:resource#fragment combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
* @param fragmentName The name of the fragment of the resource
*/
public ResourceUrn(String moduleName, String resourceName, String fragmentName) {
this(new Name(moduleName), new Name(resourceName), new Name(fragmentName), false);
}
/**
* Creates a ModuleUri with the given module:resource#fragment(!instance) combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
* @param fragmentName The name of the fragment of the resource
* @param instance Whether this urn identifies an instance
*/
public ResourceUrn(String moduleName, String resourceName, String fragmentName, boolean instance) {
this(new Name(moduleName), new Name(resourceName), new Name(fragmentName), instance);
}
/**
* Creates a ModuleUri with the given module:resource#fragment combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
* @param fragmentName The name of the fragment of the resource
*/
public ResourceUrn(Name moduleName, Name resourceName, Name fragmentName) {
this(moduleName, resourceName, fragmentName, false);
}
/**
* Creates a ModuleUri with the given module:resource#fragment(!instance) combo
*
* @param moduleName The name of the module the resource belongs to
* @param resourceName The name of the resource itself
* @param fragmentName The name of the fragment of the resource
* @param instance Whether this urn identifies an instance
*/
public ResourceUrn(Name moduleName, Name resourceName, Name fragmentName, boolean instance) {
Preconditions.checkArgument(moduleName != null && !moduleName.isEmpty(), "moduleName must not be null or empty");
Preconditions.checkArgument(resourceName != null && !resourceName.isEmpty(), "resourceName must not be null or empty");
this.moduleName = moduleName;
this.resourceName = resourceName;
this.fragmentName = fragmentName;
this.instance = instance;
}
/**
* Creates a ModuleUrn from a string in the format "module:object(#fragment)(!instance)".
*
* @param urn The urn to parse
* @throws org.terasology.assets.exceptions.InvalidUrnException if the string is not a valid resource urn
*/
public ResourceUrn(String urn) {
Matcher match = URN_PATTERN.matcher(urn);
if (match.matches()) {
moduleName = new Name(match.group(1));
resourceName = new Name(match.group(2));
if (!Strings.isNullOrEmpty(match.group(3))) {
fragmentName = new Name(match.group(3));
} else {
fragmentName = Name.EMPTY;
}
instance = !Strings.isNullOrEmpty(match.group(4));
} else {
throw new InvalidUrnException("Invalid Urn: '" + urn + "'");
}
}
/**
* @param urn The string to check for validity
* @return Whether urn is a valid ResourceUrn
*/
public static boolean isValid(String urn) {
return URN_PATTERN.matcher(urn).matches();
}
/**
* @return The module name part of the urn. This identifies the module that the resource belongs to.
*/
public Name getModuleName() {
return moduleName;
}
/**
* @return The resource name part of the urn. This identifies the resource itself.
*/
public Name getResourceName() {
return resourceName;
}
/**
* @return The fragment name part of the urn. This identifies a sub-part of the resource.
*/
public Name getFragmentName() {
return fragmentName;
}
/**
* @return Whether this urn identifies an independent copy of the resource
*/
public boolean isInstance() {
return instance;
}
/**
* @return The root of the ResourceUrn, without the fragment name or instance marker.
*/
public ResourceUrn getRootUrn() {
if (fragmentName.isEmpty() && !isInstance()) {
return this;
}
return new ResourceUrn(moduleName, resourceName);
}
/**
* @return If this urn is an instance, returns the urn without the instance marker. Otherwise this urn.
*/
public ResourceUrn getParentUrn() {
if (isInstance()) {
return new ResourceUrn(moduleName, resourceName, fragmentName);
} else {
return this;
}
}
/**
* @return This instance urn version of this urn. If this urn is already an instance, this urn is returned.
*/
public ResourceUrn getInstanceUrn() {
if (!isInstance()) {
return new ResourceUrn(moduleName, resourceName, fragmentName, true);
} else {
return this;
}
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(moduleName);
stringBuilder.append(RESOURCE_SEPARATOR);
stringBuilder.append(resourceName);
if (!fragmentName.isEmpty()) {
stringBuilder.append(FRAGMENT_SEPARATOR);
stringBuilder.append(fragmentName);
}
if (instance) {
stringBuilder.append(INSTANCE_INDICATOR);
}
return stringBuilder.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ResourceUrn) {
ResourceUrn other = (ResourceUrn) obj;
return Objects.equal(moduleName, other.moduleName) && Objects.equal(resourceName, other.resourceName)
&& Objects.equal(fragmentName, other.fragmentName) && instance == other.instance;
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(fragmentName, resourceName, moduleName);
}
@Override
public int compareTo(@Nonnull ResourceUrn o) {
int result = moduleName.compareTo(o.getModuleName());
if (result == 0) {
result = resourceName.compareTo(o.getResourceName());
}
if (result == 0) {
result = fragmentName.compareTo(o.getFragmentName());
}
if (result == 0) {
if (instance && !o.instance) {
result = 1;
} else if (!instance && o.instance) {
result = -1;
}
}
return result;
}
}