Miscellaneous.java
/*
* *********************************************************************************************************************
*
* blueMarine II: Semantic Media Centre
* http://tidalwave.it/projects/bluemarine2
*
* Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
*
* *********************************************************************************************************************
*
* 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.
*
* *********************************************************************************************************************
*
* git clone https://bitbucket.org/tidalwave/bluemarine2-src
* git clone https://github.com/tidalwave-it/bluemarine2-src
*
* *********************************************************************************************************************
*/
package it.tidalwave.bluemarine2.util;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.text.Normalizer;
//import java.util.regex.Pattern;
import java.util.Objects;
import java.util.stream.Stream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import static java.util.stream.Collectors.toList;
import static java.text.Normalizer.Form.*;
//import java.util.regex.Matcher;
import static lombok.AccessLevel.PRIVATE;
/***********************************************************************************************************************
*
* @author Fabrizio Giudici
*
**********************************************************************************************************************/
@NoArgsConstructor(access = PRIVATE) @Slf4j
public final class Miscellaneous
{
private static final Normalizer.Form NATIVE_FORM;
// private static final Pattern PATTERN_EXTENSION = Pattern.compile("(\\.[^.]+)$");
static
{
final String osName = System.getProperty("os.name").toLowerCase();
switch (osName)
{
case "linux":
NATIVE_FORM = NFC;
break;
case "mac os x":
NATIVE_FORM = NFD;
break;
case "windows":
NATIVE_FORM = NFD; // FIXME: just guessing
break;
default:
throw new ExceptionInInitializerError("Unknown o.s.: " + osName);
}
log.info(">>>> Charset normalizer form: {}", NATIVE_FORM);
}
/*******************************************************************************************************************
*
*
*
******************************************************************************************************************/
@Nullable
public static String normalizedToNativeForm (@Nullable final String string)
{
return (string == null) ? null : Normalizer.normalize(string, NATIVE_FORM);
}
/*******************************************************************************************************************
*
* Takes a path, and in case it can't be resolved, it tries to replace with an equivalent representation of an
* existing path, with the native form of character encoding (i.e. the one used by the file system).
* If there is no normalized path to replace with, the original path is returned.
* Note that this method is I/O heavy, as it must access the file system.
* FIXME: what about using a cache?
*
* See http://askubuntu.com/questions/533690/rsync-with-special-character-files-not-working-between-mac-and-linux
*
* @param path the path
* @return the normalized path
*
******************************************************************************************************************/
@Nonnull
public static Path normalizedPath (@Nonnull final Path path)
throws IOException
{
// log.trace("normalizedPath({}", path);
if (Files.exists(path))
{
return path;
}
Path pathSoFar = Paths.get("/");
for (final Path element : path)
{
// log.trace(">>>> pathSoFar: {} element: {}", pathSoFar, element);
final Path resolved = pathSoFar.resolve(element);
if (Files.exists(resolved))
{
pathSoFar = resolved;
}
else
{
// FIXME: refactor with lambdas
try (final Stream<Path> stream = Files.list(pathSoFar))
{
boolean found = false;
for (final Path child : stream.collect(toList()))
{
final Path childName = child.getFileName();
found = Objects.equals(normalizedToNativeForm(element.toString()),
normalizedToNativeForm(childName.toString()));
// log.trace(">>>> original: {} found: {} same: {}", element, childName, found);
if (found)
{
pathSoFar = pathSoFar.resolve(childName);
break;
}
}
if (!found)
{
return path; // fail
}
}
}
}
return pathSoFar;
}
/*******************************************************************************************************************
*
*
*
******************************************************************************************************************/
@Nonnull
public static File toFileBMT46 (@Nonnull final Path path)
throws IOException
{
File file = path.toFile();
if (probeBMT46(path))
{
file = File.createTempFile("bmt46-", "." + extensionOf(path));
file.deleteOnExit();
log.warn("Workaround for BMT-46: copying {} to temporary file: {}", path, file);
Files.copy(path, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
return file;
}
/*******************************************************************************************************************
*
*
*
******************************************************************************************************************/
@Nonnull
private static String extensionOf (@Nonnull final Path path)
{
final int i = path.toString().lastIndexOf('.');
return (i < 0) ? "" : path.toString().substring(i + 1);
// final Matcher matcher = PATTERN_EXTENSION.matcher(path.toString()); TODO
// return matcher.matches() ? matcher.group(0) : "";
}
/*******************************************************************************************************************
*
*
*
******************************************************************************************************************/
private static boolean probeBMT46 (@Nonnull final Path path)
{
return Files.exists(path) && !path.toFile().exists();
}
}