/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2005-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2007-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.coverage.sql;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import org.geotoolkit.internal.sql.table.DefaultEntry;
/**
* A series of coverages sharing common characteristics in a {@linkplain LayerEntry layer entry}.
* A layer often regroup all coverages in a single series, but in some cases a layer may contains
* more than one series. For example a layer of <cite>Sea Surface Temperature</cite> (SST) from
* Nasa <cite>Pathfinder</cite> can be subdivised in two series:
* <p>
* <ul>
* <li>Final release of historical data. Those data are often two years old.</li>
* <li>More recent but not yet definitive data.</li>
* </ul>
* <p>
* In most cases it is sufficient to work with {@linkplain LayerEntry layer entry}
* as a whole without the need to go down to the {@code SeriesEntry}.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.10
*
* @since 3.10 (derived from Seagis)
* @module
*/
final class SeriesEntry extends DefaultEntry {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 7119677073947466143L;
/**
* The value to put in {@link #protocol} for files.
*/
static final String FILE_PROTOCOL = "file";
/**
* The protocol in a URL, or {@code "file"} if the files should be read locally.
* The protocol may be for example {@code "file"}, {@code "ftp"} or {@code "dods"}
* (the later is for OpenDAP).
*/
final String protocol;
/**
* The host in a URL, or {@code null} if the files should be read locally.
*/
private final String host;
/**
* The directory which contains the data files for this series.
* The path separator is Unix slash, never the Windows backslash.
* May be an empty string but never {@code null}.
*/
final String path;
/**
* The extension to add to filenames, not including the dot character.
*/
private final String extension;
/**
* The format of all coverages in this series.
*/
final FormatEntry format;
/**
* Creates a new series entry. The identifier must be an instance of {@link Integer} rather
* than an arbitrary {@link Comparable} because some methods assume integer type. A search
* on usage of {@link #getIdentifier()} will list them.
*
* @param identifier The identifier for this series.
* @param root The root directory or URL, or {@code null} if none.
* @param pathname The relative or absolute directory which contains the data files for this series.
* @param extension The extension to add to filenames, not including the dot character.
* @param format The format of all coverages in this series.
* @param remarks The remarks, or {@code null} if none.
*/
protected SeriesEntry(final Integer identifier, final String root, final String pathname,
final String extension, final FormatEntry format, final String remarks)
{
super(identifier, remarks);
this.extension = extension;
this.format = format;
/*
* Checks if the pathname contains a URL host. If it does, then the URL will have
* precedence over the root parameter. In such case the root parameter is ignored.
*/
int split = pathname.indexOf("://");
if (split >= 0) {
protocol = pathname.substring(0, split).trim();
split += 3;
if (protocol.equalsIgnoreCase(FILE_PROTOCOL)) {
host = null;
path = pathname.substring(split);
// Path is likely to contains a leading '/' since the syntax is usualy "file:///".
} else {
final int base = split;
split = pathname.indexOf('/', split);
if (split < 0) {
// No path after the protocol (e.g. "dods://www.foo.org").
host = pathname.substring(base);
path = "";
} else {
host = pathname.substring(base, split);
path = pathname.substring(++split);
}
}
return;
}
/*
* Below this point, we known that the pathname is not an URL.
* but maybe the "root" parameter is an URL. Checks it now.
*/
if (root == null) {
protocol = FILE_PROTOCOL;
host = null;
path = pathname;
return;
}
split = root.indexOf("://");
if (split < 0) {
protocol = FILE_PROTOCOL;
host = null;
split = 0; // Used for computing 'path' later.
} else {
protocol = root.substring(0, split).trim();
split += 3;
if (protocol.equalsIgnoreCase(FILE_PROTOCOL)) {
host = null;
} else {
final int base = split;
split = root.indexOf('/', split);
if (split < 0) {
host = root.substring(base);
path = pathname;
return;
}
host = root.substring(base, split++);
}
}
final boolean isAbsolute = pathname.startsWith("/");
if (isAbsolute) {
path = pathname;
} else {
final String directory = root.substring(split);
final StringBuilder buffer = new StringBuilder(directory);
if (!directory.endsWith("/")) {
buffer.append('/');
}
path = buffer.append(pathname).toString();
}
}
/**
* Returns the identifier of this series.
*/
@Override
public Integer getIdentifier() {
return (Integer) identifier;
}
/**
* Returns the given filename as a {@link File} augmented with series-dependent
* {@linkplain File#getParent parent} and extension. The returned file should be
* {@linkplain File#isAbsolute absolute}. If it is not, then there is probably no
* {@linkplain org.constellation.catalog.ConfigurationKey#ROOT_DIRECTORY root directory}
* set and consequently the file is probably not accessible locally. In such case,
* consider using {@link #uri(String)} instead.
*
* @param filename The filename, not including the extension.
* @return The file.
*/
public File file(String filename) {
if (extension != null && !extension.isEmpty()) {
filename = filename + '.' + extension;
}
return new File(path, filename);
}
/**
* Returns the given filename as a {@link URI} augmented with series-dependent
* {@linkplain URI#getHost host}, parent and extension.
*
* @param filename The filename, not including the extension.
* @return The file.
* @throws URISyntaxException if the URI can not be created from the informations
* provided in the database.
*/
public URI uri(final String filename) throws URISyntaxException {
if (host == null) {
return file(filename).toURI();
}
final StringBuilder buffer = new StringBuilder(path.length() + 8);
if (!path.startsWith("/")) {
buffer.append('/');
}
buffer.append(path);
if (!path.endsWith("/") && !filename.startsWith("/")) {
buffer.append('/');
}
buffer.append(filename);
if (extension != null && !extension.isEmpty()) {
buffer.append('.').append(extension);
}
return new URI(protocol, host, buffer.toString(), null);
}
/**
* Compares this series entry with the specified object for equality.
*/
@Override
public boolean equals(final Object object) {
if (object == this) {
return true;
}
if (super.equals(object)) {
final SeriesEntry that = (SeriesEntry) object;
return Objects.equals(this.protocol, that.protocol) &&
Objects.equals(this.host, that.host) &&
Objects.equals(this.path, that.path) &&
Objects.equals(this.extension, that.extension) &&
Objects.equals(this.format, that.format);
}
return false;
}
}