/* * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.data.codec.gson; import com.google.common.base.Preconditions; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.Iterator; import java.util.List; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Union codec based with a pre-calculated set of sub-types. * * @author Robert Varga * * @param <T> Data representation type */ abstract class UnionJSONCodec<T> implements JSONCodec<T> { private static final class Diverse extends UnionJSONCodec<Object> { Diverse(final List<JSONCodec<?>> codecs) { super(codecs); } @Override public Class<Object> getDataType() { return Object.class; } } private static final class SingleType<T> extends UnionJSONCodec<T> { private final Class<T> dataClass; SingleType(final Class<T> dataClass, final List<JSONCodec<?>> codecs) { super(codecs); this.dataClass = Preconditions.checkNotNull(dataClass); } @Override public Class<T> getDataType() { return dataClass; } } private static final Logger LOG = LoggerFactory.getLogger(UnionJSONCodec.class); private final List<JSONCodec<?>> codecs; UnionJSONCodec(final List<JSONCodec<?>> codecs) { this.codecs = ImmutableList.copyOf(codecs); } static UnionJSONCodec<?> create(final UnionTypeDefinition type, final List<JSONCodec<?>> codecs) { final Iterator<JSONCodec<?>> it = codecs.iterator(); Verify.verify(it.hasNext(), "Union %s has no subtypes", type); Class<?> dataClass = it.next().getDataType(); while (it.hasNext()) { final Class<?> next = it.next().getDataType(); if (!dataClass.equals(next)) { LOG.debug("Type {} has diverse data classes: {} and {}", type, dataClass, next); return new Diverse(codecs); } } LOG.debug("Type {} has single data class {}", type, dataClass); return new SingleType<>(dataClass, codecs); } @Override public final T parseValue(final Object ctx, final String str) { for (JSONCodec<?> codec : codecs) { final Object ret; try { ret = codec.parseValue(ctx, str); } catch (RuntimeException e) { LOG.debug("Codec {} did not accept input '{}'", codec, str, e); continue; } return getDataType().cast(ret); } throw new IllegalArgumentException("Invalid value \"" + str + "\" for union type."); } @Override public final void writeValue(final JsonWriter ctx, final T value) throws IOException { for (JSONCodec<?> codec : codecs) { if (!codec.getDataType().isInstance(value)) { LOG.debug("Codec {} cannot accept input {}, skipping it", codec, value); continue; } @SuppressWarnings("unchecked") final JSONCodec<Object> objCodec = (JSONCodec<Object>) codec; try { objCodec.writeValue(ctx, value); return; } catch (RuntimeException e) { LOG.debug("Codec {} failed to serialize {}", codec, value, e); } } throw new IllegalArgumentException("No codecs could serialize" + value); } }