/** * This file is part of git-as-svn. It is subject to the license terms * in the LICENSE file found in the top-level directory of this distribution * and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn, * including this file, may be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ package svnserver.parser; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import svnserver.parser.token.*; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Parse data from class. * * @author Artem V. Navrotskiy <bozaro@users.noreply.github.com> */ public final class MessageParser { private interface Parser { @NotNull Object parse(@Nullable SvnServerParser tokenParser) throws IOException; } @NotNull private static final byte[] emptyBytes = {}; @NotNull private static final int[] emptyInts = {}; @NotNull private static final Map<Class<?>, Parser> parsers; static { parsers = new HashMap<>(); parsers.put(String.class, MessageParser::parseString); parsers.put(byte[].class, MessageParser::parseBinary); parsers.put(int.class, MessageParser::parseInt); parsers.put(int[].class, MessageParser::parseInts); parsers.put(boolean.class, MessageParser::parseBool); } @NotNull public static <T> T parse(@NotNull Class<T> type, @Nullable SvnServerParser tokenParser) throws IOException { Parser typeParser = parsers.get(type); if (typeParser != null) { //noinspection unchecked return (T) typeParser.parse(tokenParser); } return parseObject(type, tokenParser); } @SuppressWarnings("unchecked") @NotNull private static <T> T parseObject(Class<T> type, @Nullable SvnServerParser tokenParser) throws IOException { if (tokenParser != null && tokenParser.readItem(ListBeginToken.class) == null) tokenParser = null; final int depth = getDepth(tokenParser); if (type.isArray()) { final List<Object> result = new ArrayList<>(); if (tokenParser != null) { while (true) { final Object element = parse(type.getComponentType(), tokenParser); if (getDepth(tokenParser) < depth) break; result.add(element); } } return (T) result.toArray((Object[]) Array.newInstance(type.getComponentType(), result.size())); } final Constructor<?>[] ctors = type.getDeclaredConstructors(); if (ctors.length != 1) { throw new IllegalStateException("Can't find parser ctor for object: " + type.getName()); } final Constructor<?> ctor = ctors[0]; final Parameter[] ctorParams = ctor.getParameters(); Object[] params = new Object[ctorParams.length]; for (int i = 0; i < params.length; ++i) { params[i] = parse(ctorParams[i].getType(), getDepth(tokenParser) == depth ? tokenParser : null); } while (tokenParser != null && getDepth(tokenParser) >= depth) { tokenParser.readToken(); } try { //noinspection unchecked return (T) ctor.newInstance(params); } catch (ReflectiveOperationException e) { throw new IllegalStateException(e); } } @NotNull private static String parseString(@Nullable SvnServerParser tokenParser) throws IOException { if (tokenParser == null) { return ""; } final TextToken token = tokenParser.readItem(TextToken.class); return token != null ? token.getText() : ""; } @NotNull private static byte[] parseBinary(@Nullable SvnServerParser tokenParser) throws IOException { if (tokenParser == null) { return emptyBytes; } final StringToken token = tokenParser.readItem(StringToken.class); return token != null ? token.getData() : emptyBytes; } private static int parseInt(@Nullable SvnServerParser tokenParser) throws IOException { if (tokenParser == null) { return 0; } final NumberToken token = tokenParser.readItem(NumberToken.class); return token != null ? token.getNumber() : 0; } private static boolean parseBool(@Nullable SvnServerParser tokenParser) throws IOException { if (tokenParser == null) { return false; } final WordToken token = tokenParser.readItem(WordToken.class); return token != null && token.getText().equals("true"); } @NotNull private static int[] parseInts(@Nullable SvnServerParser tokenParser) throws IOException { if (tokenParser == null) { return emptyInts; } if (tokenParser.readItem(ListBeginToken.class) != null) { final List<Integer> result = new ArrayList<>(); while (true) { final NumberToken token = tokenParser.readItem(NumberToken.class); if (token == null) break; result.add(token.getNumber()); } final int[] array = new int[result.size()]; for (int i = 0; i < array.length; ++i) { array[i] = result.get(i); } return array; } return emptyInts; } private static int getDepth(@Nullable SvnServerParser tokenParser) { return tokenParser == null ? -1 : tokenParser.getDepth(); } }