public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) {
    this.counter = new MessageCounter();
    this.schemaContext = schemaContext;
    parserFactory =
        DomToNormalizedNodeParserFactory.getInstance(
            XmlUtils.DEFAULT_XML_CODEC_PROVIDER, schemaContext, strictParsing);

    mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), QNAME_FUNCTION);
    mappedNotifications = Multimaps.index(schemaContext.getNotifications(), QNAME_NOREV_FUNCTION);
  }
  @Override
  public synchronized DOMNotification toNotification(final NetconfMessage message) {
    final Map.Entry<Date, XmlElement> stripped = stripNotification(message);
    final QName notificationNoRev;
    try {
      notificationNoRev =
          QName.create(stripped.getValue().getNamespace(), stripped.getValue().getName())
              .withoutRevision();
    } catch (final MissingNameSpaceException e) {
      throw new IllegalArgumentException(
          "Unable to parse notification " + message + ", cannot find namespace", e);
    }

    final Collection<NotificationDefinition> notificationDefinitions =
        mappedNotifications.get(notificationNoRev);
    Preconditions.checkArgument(
        notificationDefinitions.size() > 0,
        "Unable to parse notification %s, unknown notification. Available notifications: %s",
        notificationDefinitions,
        mappedNotifications.keySet());

    // FIXME if multiple revisions for same notifications are present, we should pick the most
    // recent. Or ?
    // We should probably just put the most recent notification versions into our map. We can expect
    // that the device sends the data according to the latest available revision of a model.
    final NotificationDefinition next = notificationDefinitions.iterator().next();

    // We wrap the notification as a container node in order to reuse the parsers and builders for
    // container node
    final ContainerSchemaNode notificationAsContainerSchemaNode =
        NetconfMessageTransformUtil.createSchemaForNotification(next);

    final Element element = stripped.getValue().getDomElement();
    final ContainerNode content;
    try {
      content =
          parserFactory
              .getContainerNodeParser()
              .parse(Collections.singleton(element), notificationAsContainerSchemaNode);
    } catch (IllegalArgumentException e) {
      throw new IllegalArgumentException(
          String.format("Failed to parse notification %s", element), e);
    }
    return new NetconfDeviceNotification(content, stripped.getKey());
  }
  @Override
  public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final SchemaPath rpc) {
    final NormalizedNode<?, ?> normalizedNode;
    final QName rpcQName = rpc.getLastComponent();
    if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpcQName)) {
      final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument());
      final ContainerSchemaNode schemaForDataRead =
          NetconfMessageTransformUtil.createSchemaForDataRead(schemaContext);
      final ContainerNode dataNode;

      try {
        dataNode =
            parserFactory
                .getContainerNodeParser()
                .parse(Collections.singleton(xmlData), schemaForDataRead);
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException(
            String.format("Failed to parse data response %s", xmlData), e);
      }

      normalizedNode =
          Builders.containerBuilder()
              .withNodeIdentifier(
                  new YangInstanceIdentifier.NodeIdentifier(
                      NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME))
              .withChild(dataNode)
              .build();
    } else {

      Map<QName, RpcDefinition> currentMappedRpcs = mappedRpcs;

      // Determine whether a base netconf operation is being invoked and also check if the device
      // exposed model for base netconf
      // If no, use pre built base netconf operations model
      final boolean needToUseBaseCtx = mappedRpcs.get(rpcQName) == null && isBaseRpc(rpcQName);
      if (needToUseBaseCtx) {
        currentMappedRpcs = MAPPED_BASE_RPCS;
      }

      final RpcDefinition rpcDefinition = currentMappedRpcs.get(rpcQName);
      Preconditions.checkArgument(
          rpcDefinition != null, "Unable to parse response of %s, the rpc is unknown", rpcQName);

      // In case no input for rpc is defined, we can simply construct the payload here
      if (rpcDefinition.getOutput() == null) {
        Preconditions.checkArgument(
            XmlElement.fromDomDocument(message.getDocument())
                .getOnlyChildElementWithSameNamespaceOptionally("ok")
                .isPresent(),
            "Unexpected content in response of rpc: %s, %s",
            rpcDefinition.getQName(),
            message);
        normalizedNode = null;
      } else {
        final Element element = message.getDocument().getDocumentElement();
        try {
          normalizedNode =
              parserFactory
                  .getContainerNodeParser()
                  .parse(Collections.singleton(element), rpcDefinition.getOutput());
        } catch (IllegalArgumentException e) {
          throw new IllegalArgumentException(
              String.format("Failed to parse RPC response %s", element), e);
        }
      }
    }
    return new DefaultDOMRpcResult(normalizedNode);
  }