001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.util.LinkedHashMap; 005import java.util.Map; 006import java.util.Optional; 007import java.util.function.Function; 008 009/** 010 * Utility class to parse various types from other values. 011 * 012 * @param <U> the type of the objects to parse 013 * @since 16184 014 */ 015public class GenericParser<U> { 016 017 protected final Map<Class<?>, Function<U, ?>> parsers; 018 019 /** 020 * Creates an empty {@code GenericParser} 021 */ 022 public GenericParser() { 023 this(new LinkedHashMap<>()); 024 } 025 026 /** 027 * Creates a new {@code GenericParser} by deeply copying {@code parser} 028 * 029 * @param parser the parser to copy 030 */ 031 public GenericParser(GenericParser<U> parser) { 032 this(new LinkedHashMap<>(parser.parsers)); 033 } 034 035 /** 036 * Creates a new {@code GenericParser} with the same parsers as the specified map 037 * 038 * @param parsers the parsers 039 */ 040 protected GenericParser(Map<Class<?>, Function<U, ?>> parsers) { 041 this.parsers = parsers; 042 } 043 044 public <T> GenericParser<U> registerParser(Class<T> type, Function<U, T> value) { 045 parsers.put(type, value); 046 return this; 047 } 048 049 /** 050 * Determines whether {@code type} can be {@linkplain #parse parsed} 051 * 052 * @param type the type 053 * @return true if {@code type} can be parsed 054 */ 055 public boolean supports(Class<?> type) { 056 return parsers.containsKey(type); 057 } 058 059 /** 060 * Parses the given {@code value} as {@code type} and returns the result 061 * 062 * @param type the type class 063 * @param value the value to parse 064 * @param <T> the type 065 * @return the parsed value for {@code string} as type {@code type} 066 * @throws UnsupportedOperationException if {@code type} is not {@linkplain #supports supported} 067 * @throws UncheckedParseException when the parsing fails 068 */ 069 @SuppressWarnings("unchecked") 070 public <T> T parse(Class<T> type, U value) { 071 final Function<U, ?> parser = parsers.get(type); 072 if (parser == null) { 073 throw new UnsupportedOperationException(type + " is not supported"); 074 } 075 try { 076 return (T) parser.apply(value); 077 } catch (RuntimeException ex) { 078 throw new UncheckedParseException("Failed to parse [" + value + "] as " + type, ex); 079 } 080 } 081 082 /** 083 * Tries to parse the given {@code value} as {@code type} and returns the result. 084 * 085 * @param type the type class 086 * @param value the value to parse 087 * @param <T> the type 088 * @return the parsed value for {@code value} as type {@code type}, 089 * or {@code Optional.empty()} (if parsing fails, or the type is not {@linkplain #supports supported}) 090 */ 091 public <T> Optional<T> tryParse(Class<T> type, U value) { 092 try { 093 return Optional.ofNullable(parse(type, value)); 094 } catch (RuntimeException ex) { 095 Logging.trace(ex); 096 return Optional.empty(); 097 } 098 } 099}