All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.adamcin.oakpal.core.Util Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright 2018 Mark Adamcin
 *
 * 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.
 */

package net.adamcin.oakpal.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.jcr.Session;

import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.Domain;
import net.adamcin.oakpal.core.jcrfacade.SessionFacade;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class Util {
    private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);

    private Util() {
        // do nothing
    }

    static final Logger ORG_JSON_LOGGER = LoggerFactory.getLogger(ProgressCheckFactory.class);

    /**
     * Package-private sentinel type for determining when the deprecated ProgressCheckFactory.newInstance(JSONObject) method is
     * overridden.
     */
    static final ProgressCheck ORG_JSON_CHECK_SENTINEL = new ProgressCheck() {
        @Override
        public Collection getReportedViolations() {
            return Collections.emptyList();
        }
    };

    static boolean isEmpty(final String value) {
        return value == null || value.isEmpty();
    }

    /**
     * Public utility method to wrap an existing session with a facade that blocks writes.
     *
     * @param session the existing session to wrap
     * @return a read-only session
     */
    public static Session wrapSessionReadOnly(final Session session) {
        return SessionFacade.findBestWrapper(session, false);
    }

    public static List getManifestHeaderValues(final Manifest manifest, final String headerName) {
        Domain domain = Domain.domain(manifest);
        Parameters params = domain.getParameters(headerName);
        return new ArrayList<>(params.keySet());
    }

    public static List resolveManifestResources(final URL manifestUrl, final List resources) {
        return resources.stream()
                .map(name -> name.contains(":") ? name : "../" + name)
                .flatMap(composeTry(Stream::of, Stream::empty,
                        (relPath) -> new URL(manifestUrl, relPath),
                        (relPath, error) ->
                                LOGGER.debug("[resolveManifestResources] malformed url: manifestUrl={}, relPath={}, error={}",
                                        manifestUrl, relPath, error.getMessage())))
                .collect(Collectors.toList());
    }

    public static Map> mapManifestHeaderResources(final String headerName, final ClassLoader classLoader) throws IOException {
        Map> map = new LinkedHashMap<>();
        Enumeration resEnum = classLoader.getResources(JarFile.MANIFEST_NAME);
        while (resEnum.hasMoreElements()) {
            URL url = resEnum.nextElement();
            try (InputStream is = url.openStream()) {
                Manifest manifest = new Manifest(is);
                List headerResources = resolveManifestResources(url, getManifestHeaderValues(manifest, headerName));
                map.put(url, headerResources);
            }
        }

        return map;
    }

    public static Map> mapManifestHeaderResources(final String headerName, final List files) throws IOException {
        Map> map = new LinkedHashMap<>();
        for (File zipFile : files) {
            if (!zipFile.exists() || zipFile.isDirectory()) {
                File manifestFile = new File(zipFile, JarFile.MANIFEST_NAME);
                if (manifestFile.exists()) {
                    try (InputStream fis = new FileInputStream(manifestFile)) {
                        Manifest manifest = new Manifest(fis);
                        final URL manifestUrl = manifestFile.toURI().toURL();
                        map.put(manifestUrl, resolveManifestResources(manifestUrl,
                                getManifestHeaderValues(manifest, headerName)));
                    }
                }
            } else {
                try (JarFile jar = new JarFile(zipFile)) {
                    Manifest manifest = jar.getManifest();
                    final URL manifestUrl = new URL(String.format("jar:%s!/%s",
                            zipFile.toURI().toURL().toExternalForm(), JarFile.MANIFEST_NAME));
                    map.put(manifestUrl, resolveManifestResources(manifestUrl,
                            getManifestHeaderValues(manifest, headerName)));
                }
            }
        }
        return map;
    }

    public static Map> mapManifestHeaderResources(final String headerName, final URL manifestUrl) throws IOException {
        Map> map = new LinkedHashMap<>();
        try (InputStream is = manifestUrl.openStream()) {
            Manifest manifest = new Manifest(is);
            map.put(manifestUrl, resolveManifestResources(manifestUrl,
                    getManifestHeaderValues(manifest, headerName)));
        }
        return map;
    }

    static ClassLoader getDefaultClassLoader() {
        return Thread.currentThread().getContextClassLoader() != null
                ? Thread.currentThread().getContextClassLoader()
                : Util.class.getClassLoader();
    }

    /**
     * Logger function to inject into a stream by way of {@code filter()} method. Calls {@code logger.debug()} with the
     * provided {@code format} string and the current stream element as a format argument.
     *
     * @param logger the logger to use
     * @param format the SLF4j format string (use curly braces for placeholders)
     * @param     the captured type parameter for the stream element
     * @return a single-argument predicate for use in a {@code Stream.filter()} method
     */
    public static  Predicate debugFilter(final Logger logger, final String format) {
        return item -> {
            logger.debug(format, item);
            return true;
        };
    }

    /**
     * Logger function to inject into a stream by way of {@code filter()} method. Calls {@code logger.trace()} with the
     * provided {@code format} string and the current stream element as a format argument.
     *
     * @param logger the logger to use
     * @param format the SLF4j format string (use curly braces for placeholders)
     * @param     the captured type parameter for the stream element
     * @return a single-argument predicate for use in a {@code Stream.filter()} method
     */
    public static  Predicate traceFilter(final Logger logger, final String format) {
        return item -> {
            logger.trace(format, item);
            return true;
        };
    }

    /**
     * @deprecated 1.2.0 org.json has been replaced with javax.json.
     * @param jsonArray the array to read objects and map to the specified type
     * @param mapper the mapper function
     * @param  the target type
     * @return the mapped list
     */
    @Deprecated
    public static  List fromJSONArray(final JSONArray jsonArray, final Function mapper) {
        List results = new ArrayList<>();
        List onlyObjects = stream(jsonArray)
                .filter(json -> json instanceof JSONObject)
                .map(JSONObject.class::cast)
                .collect(Collectors.toList());

        for (JSONObject json : onlyObjects) {
            results.add(mapper.apply(json));
        }

        return Collections.unmodifiableList(results);
    }

    /**
     * @deprecated 1.2.0 org.json has been replaced with javax.json.
     * @param jsonArray the array to read objects and map to the specified type
     * @return the mapped list
     */
    @Deprecated
    public static List fromJSONArrayAsStrings(final JSONArray jsonArray) {
        return stream(jsonArray)
                .map(String::valueOf)
                .collect(Collectors.toList());
    }

    @Deprecated
    public static  List fromJSONArrayParsed(final JSONArray jsonArray,
                                                  final TryFunction parser,
                                                  final BiConsumer errorConsumer) {
        return stream(jsonArray)
                .map(String::valueOf)
                .flatMap(composeTry(Stream::of, Stream::empty, parser, errorConsumer))
                .collect(Collectors.toList());
    }

    @Deprecated
    public static Stream stream(final JSONArray jsonArray) {
        if (jsonArray != null) {
            return StreamSupport.stream(jsonArray.spliterator(), false);
        } else {
            return Stream.empty();
        }
    }

    /**
     * Composes four lambdas into a single function for use with flatMap() defined by {@link Stream},
     * {@link java.util.Optional}, etc. Useful for eliminating clumsy try/catch blocks from lambdas.
     *
     * @param monadUnit the "unit" (or "single") function defined by the appropriate monad. I.E. Stream::of,
     *                  Optional::of, or Optional::ofNullable.
     * @param monadZero the "zero" (or "empty") function defined by the appropriate monad, as in Stream::empty,
     *                  or Optional::empty
     * @param onElement some function that produces type {@code R} when given an object of type {@code T}, or fails
     *                  with an Exception.
     * @param onError   an optional consumer function to perform some logic when the parser function throws.
     *                  Receives both the failing input element and the caught Exception.
     * @param        The captured monad type, which must match the return types of the {@code monadUnit} and
     *                  {@code monadZero} functions, but which is not involved in the {@code onElement} or
     *                  {@code onError} functions.
     * @param        The input type mapped by the monad, i.e. the String type in {@code Stream}.
     * @param        The output type mapped by the monad, i.e. the URL type in {@code Stream}.
     * @return a flatMappable function
     */
    public static  Function composeTry(final Function monadUnit,
                                                      final Supplier monadZero,
                                                      final TryFunction onElement,
                                                      final BiConsumer onError) {
        final BiConsumer consumeError = onError != null
                ? onError
                : (e, t) -> {
        };

        return (element) -> {
            try {
                return monadUnit.apply(onElement.tryApply(element));
            } catch (final Exception error) {
                consumeError.accept(element, error);
                return monadZero.get();
            }
        };
    }

    /**
     * Functional interface similar to {@link Function}, but which allows for throwing exceptions. Use with
     * {@link #composeTry(Function, Supplier, TryFunction, BiConsumer)} to make suitable for {@code flatMap}.
     *
     * @param  input type
     * @param  output type
     */
    @FunctionalInterface
    public interface TryFunction {
        R tryApply(T element) throws Exception;
    }

    public static  Function> optFunc(final Function inputFunc) {
        return ((Function>) Optional::ofNullable).compose(inputFunc);
    }

    public static  Function compose(final Function before, final Function after) {
        return after.compose(before);
    }

}