Miscellaneous.java

  1. /*
  2.  * *********************************************************************************************************************
  3.  *
  4.  * blueMarine II: Semantic Media Centre
  5.  * http://tidalwave.it/projects/bluemarine2
  6.  *
  7.  * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
  8.  *
  9.  * *********************************************************************************************************************
  10.  *
  11.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  12.  * the License. You may obtain a copy of the License at
  13.  *
  14.  *     http://www.apache.org/licenses/LICENSE-2.0
  15.  *
  16.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  17.  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
  18.  * specific language governing permissions and limitations under the License.
  19.  *
  20.  * *********************************************************************************************************************
  21.  *
  22.  * git clone https://bitbucket.org/tidalwave/bluemarine2-src
  23.  * git clone https://github.com/tidalwave-it/bluemarine2-src
  24.  *
  25.  * *********************************************************************************************************************
  26.  */
  27. package it.tidalwave.bluemarine2.util;

  28. import javax.annotation.Nonnull;
  29. import javax.annotation.Nullable;
  30. import java.text.Normalizer;
  31. //import java.util.regex.Pattern;
  32. import java.util.Objects;
  33. import java.util.stream.Stream;
  34. import java.io.File;
  35. import java.io.IOException;
  36. import java.nio.file.Files;
  37. import java.nio.file.Path;
  38. import java.nio.file.Paths;
  39. import java.nio.file.StandardCopyOption;
  40. import lombok.NoArgsConstructor;
  41. import lombok.extern.slf4j.Slf4j;
  42. import static java.util.stream.Collectors.toList;
  43. import static java.text.Normalizer.Form.*;
  44. //import java.util.regex.Matcher;
  45. import static lombok.AccessLevel.PRIVATE;

  46. /***********************************************************************************************************************
  47.  *
  48.  * @author  Fabrizio Giudici
  49.  *
  50.  **********************************************************************************************************************/
  51. @NoArgsConstructor(access = PRIVATE) @Slf4j
  52. public final class Miscellaneous
  53.   {
  54.     private static final Normalizer.Form NATIVE_FORM;

  55. //    private static final Pattern PATTERN_EXTENSION = Pattern.compile("(\\.[^.]+)$");

  56.     static
  57.       {
  58.         final String osName = System.getProperty("os.name").toLowerCase();

  59.         switch (osName)
  60.           {
  61.             case "linux":
  62.                 NATIVE_FORM = NFC;
  63.                 break;

  64.             case "mac os x":
  65.                 NATIVE_FORM = NFD;
  66.                 break;

  67.             case "windows":
  68.                 NATIVE_FORM = NFD; // FIXME: just guessing
  69.                 break;

  70.             default:
  71.                 throw new ExceptionInInitializerError("Unknown o.s.: " + osName);
  72.           }

  73.         log.info(">>>> Charset normalizer form: {}", NATIVE_FORM);
  74.       }

  75.     /*******************************************************************************************************************
  76.      *
  77.      *
  78.      *
  79.      ******************************************************************************************************************/
  80.     @Nullable
  81.     public static String normalizedToNativeForm (@Nullable final String string)
  82.       {
  83.         return (string == null) ? null : Normalizer.normalize(string, NATIVE_FORM);
  84.       }

  85.     /*******************************************************************************************************************
  86.      *
  87.      * Takes a path, and in case it can't be resolved, it tries to replace with an equivalent representation of an
  88.      * existing path, with the native form of character encoding (i.e. the one used by the file system).
  89.      * If there is no normalized path to replace with, the original path is returned.
  90.      * Note that this method is I/O heavy, as it must access the file system.
  91.      * FIXME: what about using a cache?
  92.      *
  93.      * See http://askubuntu.com/questions/533690/rsync-with-special-character-files-not-working-between-mac-and-linux
  94.      *
  95.      * @param   path    the path
  96.      * @return          the normalized path
  97.      *
  98.      ******************************************************************************************************************/
  99.     @Nonnull
  100.     public static Path normalizedPath (@Nonnull final Path path)
  101.       throws IOException
  102.       {
  103. //        log.trace("normalizedPath({}", path);

  104.         if (Files.exists(path))
  105.           {
  106.             return path;
  107.           }

  108.         Path pathSoFar = Paths.get("/");

  109.         for (final Path element : path)
  110.           {
  111. //            log.trace(">>>> pathSoFar: {} element: {}", pathSoFar, element);
  112.             final Path resolved = pathSoFar.resolve(element);

  113.             if (Files.exists(resolved))
  114.               {
  115.                 pathSoFar = resolved;
  116.               }
  117.             else
  118.               {
  119.                 // FIXME: refactor with lambdas
  120.                 try (final Stream<Path> stream = Files.list(pathSoFar))
  121.                   {
  122.                     boolean found = false;

  123.                     for (final Path child : stream.collect(toList()))
  124.                       {
  125.                         final Path childName = child.getFileName();
  126.                         found = Objects.equals(normalizedToNativeForm(element.toString()),
  127.                                                normalizedToNativeForm(childName.toString()));
  128. //                        log.trace(">>>> original: {} found: {} same: {}", element, childName, found);

  129.                         if (found)
  130.                           {
  131.                             pathSoFar = pathSoFar.resolve(childName);
  132.                             break;
  133.                           }
  134.                       }

  135.                     if (!found)
  136.                       {
  137.                         return path; // fail
  138.                       }
  139.                   }
  140.               }
  141.           }

  142.         return pathSoFar;
  143.       }

  144.     /*******************************************************************************************************************
  145.      *
  146.      *
  147.      *
  148.      ******************************************************************************************************************/
  149.     @Nonnull
  150.     public static File toFileBMT46 (@Nonnull final Path path)
  151.       throws IOException
  152.       {
  153.         File file = path.toFile();

  154.         if (probeBMT46(path))
  155.           {
  156.             file = File.createTempFile("bmt46-", "." + extensionOf(path));
  157.             file.deleteOnExit();
  158.             log.warn("Workaround for BMT-46: copying {} to temporary file: {}", path, file);
  159.             Files.copy(path, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
  160.           }

  161.         return file;
  162.       }

  163.     /*******************************************************************************************************************
  164.      *
  165.      *
  166.      *
  167.      ******************************************************************************************************************/
  168.     @Nonnull
  169.     private static String extensionOf (@Nonnull final Path path)
  170.       {
  171.         final int i = path.toString().lastIndexOf('.');
  172.         return (i < 0) ? "" : path.toString().substring(i + 1);
  173. //        final Matcher matcher = PATTERN_EXTENSION.matcher(path.toString()); TODO
  174. //        return matcher.matches() ? matcher.group(0) : "";
  175.       }

  176.     /*******************************************************************************************************************
  177.      *
  178.      *
  179.      *
  180.      ******************************************************************************************************************/
  181.     private static boolean probeBMT46 (@Nonnull final Path path)
  182.       {
  183.         return Files.exists(path) && !path.toFile().exists();
  184.       }
  185.   }