001/*
002 * Extra Bnd Repository Plugins
003 * Copyright (C) 2019  Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package de.mnl.osgi.bnd.maven;
020
021import java.lang.reflect.UndeclaredThrowableException;
022import java.util.concurrent.Callable;
023import java.util.regex.Pattern;
024import java.util.stream.Stream;
025
026/**
027 * The Class Utils.
028 */
029public final class RepositoryUtils {
030
031    /** The Constant LIST_ITEM_SEPARATOR. */
032    public static final Pattern LIST_ITEM_SEPARATOR
033        = Pattern.compile("\\s*,\\s*");
034
035    private RepositoryUtils() {
036    }
037
038    /**
039     * Itemize list.
040     *
041     * @param list the list
042     * @return the stream
043     */
044    public static Stream<String> itemizeList(String list) {
045        if (list == null) {
046            return Stream.empty();
047        }
048        return LIST_ITEM_SEPARATOR.splitAsStream(list);
049    }
050
051    /**
052     * Run ignoring any throwable.
053     *
054     * @param runnable the function to be executed
055     */
056    @SuppressWarnings({ "PMD.AvoidCatchingThrowable", "PMD.EmptyCatchBlock",
057        "PMD.AvoidDuplicateLiterals" })
058    public static void runIgnoring(ThrowingRunnable runnable) {
059        try {
060            runnable.run();
061        } catch (Throwable t) {
062            // Ignored
063        }
064    }
065
066    /**
067     * Run ignoring any throwable. If an exception occurs, the
068     * fallback value is returned.
069     *
070     * @param <T> the return type
071     * @param callable the function to be executed
072     * @param fallback the fallback value
073     * @return the t
074     */
075    @SuppressWarnings({ "PMD.AvoidCatchingThrowable", "PMD.EmptyCatchBlock" })
076    public static <T> T runIgnoring(Callable<T> callable, T fallback) {
077        try {
078            return callable.call();
079        } catch (Throwable t) {
080            return fallback;
081        }
082    }
083
084    /**
085     * A runnable that may throw an exception.
086     */
087    @FunctionalInterface
088    public interface ThrowingRunnable {
089
090        /**
091         * The operation to run.
092         *
093         * @throws Exception the exception
094         */
095        @SuppressWarnings("PMD.SignatureDeclareThrowsException")
096        void run() throws Exception;
097    }
098
099    /**
100     * Converts any exception thrown by the supplier to an
101     * {@link UndeclaredThrowableException}.
102     *
103     * @param <T> the return value
104     * @param supplier the supplier
105     * @return the t
106     */
107    @SuppressWarnings({ "PMD.AvoidCatchingThrowable",
108        "PMD.AvoidCatchingGenericException", "PMD.AvoidRethrowingException",
109        "PMD.AvoidDuplicateLiterals" })
110    public static <T> T unthrow(Callable<T> supplier) {
111        try {
112            return supplier.call();
113        } catch (RuntimeException e) {
114            throw e;
115        } catch (Throwable t) {
116            throw new UndeclaredThrowableException(t);
117        }
118    }
119
120    /**
121     * Converts any exception thrown by the runnable to an
122     * {@link UndeclaredThrowableException}.
123     *
124     * @param runnable the runnable
125     */
126    @SuppressWarnings({ "PMD.AvoidCatchingGenericException",
127        "PMD.AvoidRethrowingException", "PMD.AvoidCatchingThrowable" })
128    public static void unthrow(ThrowingRunnable runnable) {
129        try {
130            runnable.run();
131        } catch (RuntimeException e) {
132            throw e;
133        } catch (Throwable t) {
134            throw new UndeclaredThrowableException(t);
135        }
136    }
137
138    /**
139     * Catches {@link UndeclaredThrowableException}s and unwraps
140     * any underlying exception of the given type.
141     *
142     * @param <T> the return type
143     * @param <E> the type of exception that is unwrapped
144     * @param rethrown the type of exception that is rethrown
145     * @param supplier the supplier
146     * @return the result from invoking the {@code supplier}
147     * @throws E the exception type
148     */
149    @SuppressWarnings({ "unchecked", "PMD.AvoidCatchingGenericException",
150        "PMD.AvoidRethrowingException" })
151    public static <T, E extends Throwable> T rethrow(Class<E> rethrown,
152            Callable<T> supplier) throws E {
153        try {
154            return supplier.call();
155        } catch (UndeclaredThrowableException e) {
156            if (rethrown
157                .isAssignableFrom(e.getUndeclaredThrowable().getClass())) {
158                throw (E) e.getUndeclaredThrowable();
159            }
160            throw e;
161        } catch (RuntimeException e) {
162            throw e;
163        } catch (Exception e) {
164            throw new UndeclaredThrowableException(e);
165        }
166    }
167
168    /**
169     * Catches {@link UndeclaredThrowableException}s and unwraps
170     * any underlying exception of the given type.
171     *
172     * @param <T> the return type
173     * @param <E1> the first exception type
174     * @param <E2> the second exception type
175     * @param rethrown1 the class of the first rethrown exception type
176     * @param rethrown2 the class of the second rethrown exception type
177     * @param function the function to be executed
178     * @return the result from invoking the {@code supplier}
179     * @throws E1 the e1
180     * @throws E2 the e2
181     */
182    @SuppressWarnings({ "unchecked", "PMD.AvoidCatchingGenericException",
183        "PMD.AvoidRethrowingException" })
184    public static <T, E1 extends Throwable, E2 extends Throwable> T
185            rethrow(Class<E1> rethrown1, Class<E2> rethrown2,
186                    Callable<T> function) throws E1, E2 {
187        try {
188            return function.call();
189        } catch (UndeclaredThrowableException e) {
190            if (rethrown1
191                .isAssignableFrom(e.getUndeclaredThrowable().getClass())) {
192                throw (E1) e.getUndeclaredThrowable();
193            }
194            if (rethrown2
195                .isAssignableFrom(e.getUndeclaredThrowable().getClass())) {
196                throw (E2) e.getUndeclaredThrowable();
197            }
198            throw e;
199        } catch (RuntimeException e) {
200            throw e;
201        } catch (Exception e) {
202            throw new UndeclaredThrowableException(e);
203        }
204    }
205
206    /**
207     * Catches {@link UndeclaredThrowableException}s and unwraps
208     * any underlying exception of the given type.
209     *
210     * @param <E> the type of exception that is unwrapped
211     * @param rethrown the type of exception that is rethrown
212     * @param runnable the runnable
213     * @throws E the exception type
214     */
215    @SuppressWarnings({ "unchecked", "PMD.AvoidCatchingGenericException" })
216    public static <E extends Throwable> void rethrow(Class<E> rethrown,
217            ThrowingRunnable runnable) throws E {
218        try {
219            runnable.run();
220        } catch (UndeclaredThrowableException e) {
221            if (rethrown
222                .isAssignableFrom(e.getUndeclaredThrowable().getClass())) {
223                throw (E) e.getUndeclaredThrowable();
224            }
225            throw e;
226        } catch (Exception e) {
227            throw new UndeclaredThrowableException(e);
228        }
229    }
230}