<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>PathNormalization.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">blueMarine II :: Persistence</a> > <a href="../index.html" class="el_bundle">it-tidalwave-bluemarine2-commons</a> > <a href="index.source.html" class="el_package">it.tidalwave.bluemarine2.util</a> > <span class="el_source">PathNormalization.java</span></div><h1>PathNormalization.java</h1><pre class="source lang-java linenums">/*
* *********************************************************************************************************************
*
* 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.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import it.tidalwave.util.annotation.VisibleForTesting;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import static java.text.Normalizer.Form.*;
/***********************************************************************************************************************
*
* See the related test for detailed information.
*
* @author Fabrizio Giudici
*
**********************************************************************************************************************/
<span class="pc bpc" id="L51" title="1 of 2 branches missed.">@UtilityClass @Slf4j</span>
<span class="nc" id="L52">public class PathNormalization</span>
{
private static final Normalizer.Form NATIVE_FORM;
static
{
<span class="fc" id="L58"> final String osName = System.getProperty("os.name").toLowerCase();</span>
<span class="pc bpc" id="L60" title="3 of 4 branches missed."> switch (osName)</span>
{
case "linux":
<span class="fc" id="L63"> NATIVE_FORM = NFC;</span>
<span class="fc" id="L64"> break;</span>
case "mac os x":
<span class="nc" id="L67"> NATIVE_FORM = NFD;</span>
<span class="nc" id="L68"> break;</span>
case "windows":
<span class="nc" id="L71"> NATIVE_FORM = NFD; // FIXME: just guessing</span>
<span class="nc" id="L72"> break;</span>
default:
<span class="nc" id="L75"> throw new ExceptionInInitializerError("Unknown o.s.: " + osName);</span>
}
<span class="fc" id="L78"> log.info("Charset normalizer form: {}", NATIVE_FORM);</span>
<span class="fc" id="L79"> }</span>
/*******************************************************************************************************************
*
* Takes a path that maps to an existing file, 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?
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 fixedPath (@Nonnull final Path path)
throws IOException
{
// log.trace("fixedPath({})", path);
<span class="fc bfc" id="L102" title="All 2 branches covered."> if (Files.exists(path)) // can be normally found, no need to process it</span>
{
<span class="fc" id="L104"> return path;</span>
}
<span class="fc" id="L107"> Path pathSoFar = Paths.get("/");</span>
<span class="fc bfc" id="L109" title="All 2 branches covered."> for (final Path segment : path.toAbsolutePath())</span>
{
// log.trace(">>>> pathSoFar: {} segment: {}", pathSoFar, segment);
<span class="fc" id="L112"> final Path resolved = pathSoFar.resolve(segment);</span>
<span class="fc bfc" id="L114" title="All 2 branches covered."> if (Files.exists(resolved))</span>
{
<span class="fc" id="L116"> pathSoFar = resolved;</span>
}
else // didn't find 'resolved' because of wrong normalisation, searching in alternative way
{
<span class="fc" id="L120"> try (final Stream<Path> stream = Files.list(pathSoFar))</span>
{
<span class="fc" id="L122"> final Optional<Path> child = stream.map(Path::getFileName)</span>
<span class="fc" id="L123"> .filter(p -> equalsNormalized(segment, p))</span>
<span class="fc" id="L124"> .findFirst();</span>
<span class="pc bpc" id="L125" title="1 of 2 branches missed."> if (child.isEmpty())</span>
{
<span class="nc" id="L127"> log.warn(">>>> fixing failed at: {}", pathSoFar);</span>
<span class="nc" id="L128"> return path;</span>
}
<span class="fc" id="L131"> pathSoFar = pathSoFar.resolve(child.get());</span>
<span class="pc bpc" id="L132" title="2 of 4 branches missed."> assert Files.exists(pathSoFar) : "Fixing failed at: " + pathSoFar;</span>
<span class="nc bnc" id="L133" title="All 2 branches missed."> }</span>
}
<span class="fc" id="L135"> }</span>
<span class="fc" id="L137"> return pathSoFar;</span>
}
/*******************************************************************************************************************
*
* Checks whether two Paths are equal after normalisation of their string representation.
*
* @param path1 the former path
* @param path2 the latter path
* @return {@code true} if they are equal
*
******************************************************************************************************************/
@VisibleForTesting static boolean equalsNormalized (@Nonnull final Path path1, @Nonnull final Path path2)
{
<span class="fc" id="L151"> return Objects.equals(normalizedToNativeForm(path1.toString()), normalizedToNativeForm(path2.toString()));</span>
}
/*******************************************************************************************************************
*
*
*
******************************************************************************************************************/
@Nullable
public static String normalizedToNativeForm (@Nullable final String string)
{
<span class="pc bpc" id="L162" title="1 of 2 branches missed."> return (string == null) ? null : Normalizer.normalize(string, NATIVE_FORM);</span>
}
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html>