content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.camel.converter.jaxb;
  
  import java.util.HashMap;
  import java.util.Map;
  
  import org.apache.camel.CamelContext;
  import org.apache.camel.spi.DataFormat;
  import org.apache.camel.spi.PropertyConfigurer;
  import org.apache.camel.spi.RestBindingJaxbDataFormatFactory;
  import org.apache.camel.spi.RestConfiguration;
  import org.apache.camel.spi.annotations.JdkService;
  import org.apache.camel.support.PluginHelper;
  import org.apache.camel.support.PropertyBindingSupport;
  
  /**
   * JAXB based {@link RestBindingJaxbDataFormatFactory}.
   */
  @JdkService(RestBindingJaxbDataFormatFactory.FACTORY)
  public class JaxbRestBindingJaxbDataFormatFactory implements RestBindingJaxbDataFormatFactory {
      @Override
      public void setupJaxb(
              CamelContext camelContext, RestConfiguration config,
              String type, Class<?> typeClass, String outType, Class<?> outTypeClass,
              DataFormat jaxb, DataFormat outJaxb)
              throws Exception {
          // lookup configurer
          PropertyConfigurer configurer = PluginHelper.getConfigurerResolver(camelContext)
                  .resolvePropertyConfigurer("jaxb-dataformat-configurer", camelContext);
          if (configurer == null) {
              throw new IllegalStateException("Cannot find configurer for dataformat: jaxb");
          }
  
          //
          // IN
          //
  
          PropertyBindingSupport.Builder builder = PropertyBindingSupport.build()
                  .withCamelContext(camelContext)
                  .withConfigurer(configurer)
                  .withTarget(jaxb);
  
          final String typeName = getTypeName(type, typeClass);
          if (typeName != null) {
              builder.withProperty("contextPath", typeName);
              builder.withProperty("contextPathIsClassName", "true");
          }
  
          setAdditionalConfiguration(config, "xml.in.", builder);
          builder.bind();
  
          //
          // OUT
          //
  
          PropertyBindingSupport.Builder outBuilder = PropertyBindingSupport.build()
                  .withCamelContext(camelContext)
                  .withConfigurer(configurer)
                  .withTarget(outJaxb);
  
          final String outTypeName = getOutTypeName(outType, outTypeClass, typeName);
  
          if (outTypeName != null) {
              outBuilder.withProperty("contextPath", outTypeName);
              outBuilder.withProperty("contextPathIsClassName", "true");
          }
  
          setAdditionalConfiguration(config, "xml.out.", outBuilder);
          outBuilder.bind();
      }
  
      private static String getOutTypeName(String outType, Class<?> outTypeClass, String typeName) {
          String outTypeName = null;
          if (outTypeClass != null) {
              outTypeName = outTypeClass.isArray() ? outTypeClass.getComponentType().getName() : outTypeClass.getName();
          } else if (outType != null) {
              outTypeName = outType.endsWith("[]") ? outType.substring(0, outType.length() - 2) : outType;
          } else if (typeName != null) {
              // fallback and use the context from the input
              outTypeName = typeName;
          }
          return outTypeName;
      }
  
      private static String getTypeName(String type, Class<?> typeClass) {
          String typeName = null;
          if (typeClass != null) {
              typeName = typeClass.isArray() ? typeClass.getComponentType().getName() : typeClass.getName();
          } else if (type != null) {
              typeName = type.endsWith("[]") ? type.substring(0, type.length() - 2) : type;
          }
          return typeName;
      }
  
      private void setAdditionalConfiguration(RestConfiguration config, String prefix, PropertyBindingSupport.Builder builder) {
          if (config.getDataFormatProperties() != null && !config.getDataFormatProperties().isEmpty()) {
              // must use a copy as otherwise the options gets removed during
              // introspection setProperties
              Map<String, Object> copy = new HashMap<>();
  
              // filter keys on prefix
              // - either its a known prefix and must match the prefix parameter
              // - or its a common configuration that we should always use
              for (Map.Entry<String, Object> entry : config.getDataFormatProperties().entrySet()) {
                  String key = entry.getKey();
                  String copyKey;
                  boolean known = isKeyKnownPrefix(key);
                  if (known) {
                      // remove the prefix from the key to use
                      copyKey = key.substring(prefix.length());
                  } else {
                      // use the key as is
                      copyKey = key;
                  }
                  if (!known || key.startsWith(prefix)) {
                      copy.put(copyKey, entry.getValue());
                  }
              }
  
              builder.withProperties(copy);
          }
      }
  
      private boolean isKeyKnownPrefix(String key) {
          return key.startsWith("json.in.") || key.startsWith("json.out.") || key.startsWith("xml.in.")
                  || key.startsWith("xml.out.");
      }
  
  }
  