blob: c309746d336fbc2f3c4cf2e18a5c217ed9efffca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.sis.storage.landsat;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.apache.sis.storage.Aggregate;
import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreProvider;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.storage.StorageConnector;
import org.apache.sis.storage.ProbeResult;
import org.apache.sis.internal.storage.Capability;
import org.apache.sis.internal.storage.StoreMetadata;
import org.apache.sis.internal.storage.URIDataStore;
import org.apache.sis.internal.storage.wkt.FirstKeywordPeek;
/**
* The provider of {@link LandsatStore} instances. Given a {@link StorageConnector} input,
* this class tries to instantiate a {@code LandsatStore}.
*
* <h2>Thread safety</h2>
* The same {@code LandsatStoreProvider} instance can be safely used by many threads without synchronization on
* the part of the caller. However the {@link LandsatStore} instances created by this factory are not thread-safe.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.2
* @since 1.1
* @module
*/
@StoreMetadata(formatName = LandsatStoreProvider.NAME,
capabilities = Capability.READ,
resourceTypes = {Aggregate.class, GridCoverageResource.class})
public class LandsatStoreProvider extends DataStoreProvider {
/**
* The format name.
*/
static final String NAME = "Landsat";
/**
* The parameter descriptor to be returned by {@link #getOpenParameters()}.
*/
private static final ParameterDescriptorGroup OPEN_DESCRIPTOR = URIDataStore.Provider.descriptor(NAME);
/**
* The object to use for verifying if the first keyword is the expected one.
*/
private static final class Peek extends FirstKeywordPeek {
/**
* The expected keyword after spaces removal.
*/
private static final String KEYWORD = "GROUP=LANDSAT_METADATA_FILE",
L1_KEYWORD = "GROUP=L1_METADATA_FILE";
/**
* The part in process of being parsed:
* <ul>
* <li>{@code KEY} (0) for the {@code "GROUP"} keyword,</li>
* <li>{@code SEPARATOR} (1) for the {@code '='} symbol,</li>
* <li>{@code VALUE} (2) for the {@code "L1_METADATA_FILE"} value.</li>
* </ul>
*/
private static final int KEY = 0, SEPARATOR = 1, VALUE = 2;
/**
* {@value #SEPARATOR} if parsing the characters after the {@code =} symbol,
* {@value #VALUE} if parsing the non-white characters after {@code =}.
*/
private int part = KEY;
/**
* Creates a new instance.
*/
Peek() {
super(KEYWORD.length());
}
/**
* Returns the path to the metadata file relative to the directory specified by user.
* This method is invoked if the user gave us the directory containing all Landsat files
* instead of the path to the metadata file.
*/
@Override
protected Path getAuxiliaryPath(final StorageConnector connector) throws DataStoreException {
return getMetadataFile(connector.getStorageAs(Path.class));
}
/**
* Returns {@code ACCEPT} if the given character is valid for a keyword.
*/
@Override
protected int isKeywordChar(final int c) {
final int s = super.isKeywordChar(c);
if (s == REJECT) {
if (c >= '0' && c <= '9' && part == VALUE) {
return ACCEPT;
}
switch (c) {
case '\t':
case ' ' : if (part < VALUE) { return IGNORE;} else break;
case '=' : if (part == KEY) {part = SEPARATOR; return ACCEPT;} else break;
}
} else {
if (part == SEPARATOR) {
part = VALUE;
}
}
return s;
}
/**
* Returns {@code true} if the given first non-white character after the keyword is one of the expected
* characters. This implementation expects the first letter of the {@code "GROUP"} keyword for the first
* sub-group (unless there is a comment line between them).
*/
@Override
protected boolean isPostKeyword(final int c) {
return c == 'G' || c == 'g' || c == COMMENT;
}
/**
* Returns the value to be returned by {@link LandsatStoreProvider#probeContent(StorageConnector)}
* for the given keyword.
*/
@Override
protected ProbeResult forKeyword(final char[] keyword, final int length) {
final String ks = new String(keyword, 0, length);
if (KEYWORD.equalsIgnoreCase(ks) || L1_KEYWORD.equalsIgnoreCase(ks)) {
return ProbeResult.SUPPORTED;
}
return ProbeResult.UNSUPPORTED_STORAGE;
}
}
/**
* Creates a new provider.
*/
public LandsatStoreProvider() {
}
/**
* Returns a generic name for this data store, used mostly in warnings or error messages.
*
* @return a short name or abbreviation for the data format.
*/
@Override
public String getShortName() {
return NAME;
}
/**
* Returns a description of all parameters accepted by this provider for opening a Landsat file.
*
* @return description of available parameters for opening a Landsat file.
*
* @since 0.8
*/
@Override
public ParameterDescriptorGroup getOpenParameters() {
return OPEN_DESCRIPTOR;
}
/**
* Returns the metadata file inside the given directory if the file exists, or {@code null} otherwise.
*
* @param directory directory to test, or {@code null} if unknown.
* @return metadata file, or {@code null} if it does not exist.
*/
static Path getMetadataFile(final Path directory) {
if (directory != null) {
final Path file = directory.resolve(Paths.get(directory.getFileName().toString().concat("_MTL.txt")));
if (Files.isRegularFile(file)) {
return file;
}
}
return null;
}
/**
* Returns {@link ProbeResult#SUPPORTED} if the given storage appears to be supported by {@link LandsatStore}.
* Returning {@code SUPPORTED} from this method does not guarantee that reading or writing will succeed, only
* that there appears to be a reasonable chance of success based on a brief inspection of the storage header.
*
* @return {@link ProbeResult#SUPPORTED} if the given storage seems to be readable as a Landsat file.
* @throws DataStoreException if an I/O error occurred.
*/
@Override
public ProbeResult probeContent(final StorageConnector connector) throws DataStoreException {
return new Peek().probeContent(this, connector);
}
/**
* Returns a {@link LandsatStore} implementation associated with this provider.
*
* @param connector information about the storage (URL, stream, <i>etc</i>).
* @return a data store implementation associated with this provider for the given storage.
* @throws DataStoreException if an error occurred while creating the data store instance.
*/
@Override
public DataStore open(final StorageConnector connector) throws DataStoreException {
return new LandsatStore(this, connector);
}
}