package org.opendaylight.controller.netconf.nettyutil.handler;

import com.google.common.base.Preconditions;
import org.openexi.proc.HeaderOptionsOutputType;
import org.openexi.proc.common.EXIOptions;
import org.openexi.proc.common.EXIOptionsException;
import org.openexi.proc.common.GrammarOptions;
import org.openexi.proc.grammars.GrammarCache;
import org.openexi.sax.EXIReader;
import org.openexi.sax.Transmogrifier;
import org.openexi.sax.TransmogrifierException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

public final class NetconfEXICodec {
    /**
     * NETCONF is XML environment, so the use of EXI cookie is not really needed. Adding it
     * decreases efficiency of encoding by adding human-readable 4 bytes "EXI$" to the head
     * of the stream. This is really useful, so let's output it now.
     */
    private static final boolean OUTPUT_EXI_COOKIE = true;
    /**
     * OpenEXI does not allow us to directly prevent resolution of external entities. In order
     * to prevent XXE attacks, we reuse a single no-op entity resolver.
     */
    private static final EntityResolver ENTITY_RESOLVER = new EntityResolver() {
        @Override
        public InputSource resolveEntity(final String publicId, final String systemId) {
            return new InputSource();
        }
    };

    /**
     * Grammar cache acts as a template and is duplicated by the Transmogrifier and the Reader
     * before use. It is safe to reuse a single instance.
     */
    private final GrammarCache exiGrammarCache;
    private final EXIOptions exiOptions;

    public NetconfEXICodec(final EXIOptions exiOptions) {
        this.exiOptions = Preconditions.checkNotNull(exiOptions);
        this.exiGrammarCache = createGrammarCache(exiOptions);
    }

    private static GrammarCache createGrammarCache(final EXIOptions exiOptions) {
        short go = GrammarOptions.DEFAULT_OPTIONS;
        if (exiOptions.getPreserveComments()) {
            go = GrammarOptions.addCM(go);
        }
        if (exiOptions.getPreserveDTD()) {
            go = GrammarOptions.addDTD(go);
        }
        if (exiOptions.getPreserveNS()) {
            go = GrammarOptions.addNS(go);
        }
        if (exiOptions.getPreservePIs()) {
            go = GrammarOptions.addPI(go);
        }

        return new GrammarCache(go);
    }

    EXIReader getReader() throws EXIOptionsException {
        final EXIReader r = new EXIReader();
        r.setPreserveLexicalValues(exiOptions.getPreserveLexicalValues());
        r.setGrammarCache(exiGrammarCache);
        r.setEntityResolver(ENTITY_RESOLVER);
        return r;
    }

    Transmogrifier getTransmogrifier() throws EXIOptionsException, TransmogrifierException {
        final Transmogrifier transmogrifier = new Transmogrifier();
        transmogrifier.setAlignmentType(exiOptions.getAlignmentType());
        transmogrifier.setBlockSize(exiOptions.getBlockSize());
        transmogrifier.setGrammarCache(exiGrammarCache);
        transmogrifier.setOutputCookie(OUTPUT_EXI_COOKIE);
        transmogrifier.setOutputOptions(HeaderOptionsOutputType.all);
        transmogrifier.setResolveExternalGeneralEntities(false);
        return transmogrifier;
    }
}

@@ -98,24 +98,158 @@
 conditions;%0A
+import com.google.common.cache.CacheBuilder;%0Aimport com.google.common.cache.CacheLoader;%0Aimport com.google.common.cache.LoadingCache;%0A
 import org.o
@@ -285,16 +285,16 @@
 utType;%0A
-
 import o
@@ -1455,16 +1455,470 @@
     %7D;%0A%0A
+    /**%0A     * Since we have a limited number of options we can have, instantiating a weak cache%0A     * will allow us to reuse instances where possible.%0A     */%0A    private static final LoadingCache%3CShort, GrammarCache%3E GRAMMAR_CACHES = CacheBuilder.newBuilder().weakValues().build(new CacheLoader%3CShort, GrammarCache%3E() %7B%0A        @Override%0A        public GrammarCache load(final Short key) %7B%0A            return new GrammarCache(key);%0A        %7D%0A    %7D);%0A%0A
     /**%0A
@@ -2900,32 +2900,43 @@
  return 
-new GrammarCache
+GRAMMAR_CACHES.getUnchecked
 (go);%0A  

maintain a cache of grammar caches