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.aries.blueprint.reflect;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
  
  import org.apache.aries.blueprint.PassThroughMetadata;
  import org.osgi.service.blueprint.reflect.BeanArgument;
  import org.osgi.service.blueprint.reflect.BeanMetadata;
  import org.osgi.service.blueprint.reflect.CollectionMetadata;
  import org.osgi.service.blueprint.reflect.ComponentMetadata;
  import org.osgi.service.blueprint.reflect.IdRefMetadata;
  import org.osgi.service.blueprint.reflect.MapMetadata;
  import org.osgi.service.blueprint.reflect.Metadata;
  import org.osgi.service.blueprint.reflect.NullMetadata;
  import org.osgi.service.blueprint.reflect.PropsMetadata;
  import org.osgi.service.blueprint.reflect.RefMetadata;
  import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
  import org.osgi.service.blueprint.reflect.ReferenceMetadata;
  import org.osgi.service.blueprint.reflect.ServiceMetadata;
  import org.osgi.service.blueprint.reflect.Target;
  import org.osgi.service.blueprint.reflect.ValueMetadata;
  
  
  /**
   * A utility class that handles cloning various polymorphic
   * bits of metadata into concrete class implementations.
   *
   * @version $Rev$, $Date$
   */
  public class MetadataUtil {
  
      public static final Comparator<BeanArgument> BEAN_COMPARATOR = new BeanArgumentComparator();
      
      static public Metadata cloneMetadata(Metadata source) {
          if (source == null) {
              return null;
          } 
          else if (source instanceof MapMetadata) {
              return new MapMetadataImpl((MapMetadata)source);
          }
          else if (source instanceof NullMetadata) {
              return NullMetadata.NULL;
          }
          else if (source instanceof PropsMetadata) {
              return new PropsMetadataImpl((PropsMetadata)source);
          }
          else if (source instanceof RefMetadata) {
              return new RefMetadataImpl((RefMetadata)source);
          }
          else if (source instanceof IdRefMetadata) {
              return new IdRefMetadataImpl((IdRefMetadata)source);
          }
          else if (source instanceof ValueMetadata) {
              return new ValueMetadataImpl((ValueMetadata)source);
          }
          else if (source instanceof BeanMetadata) {
              return new BeanMetadataImpl((BeanMetadata)source);
          }
          else if (source instanceof ReferenceListMetadata) {
              return new ReferenceListMetadataImpl((ReferenceListMetadata)source);
          }
          else if (source instanceof ServiceMetadata) {
              return new ServiceMetadataImpl((ServiceMetadata)source);
          }
          else if (source instanceof ReferenceMetadata) {
              return new ReferenceMetadataImpl((ReferenceMetadata)source);
          }
          else if (source instanceof CollectionMetadata) {
              return new CollectionMetadataImpl((CollectionMetadata)source);
          }
          else if (source instanceof PassThroughMetadata) {
              return new PassThroughMetadataImpl((PassThroughMetadata)source);
          }
  
          throw new RuntimeException("Unknown Metadata type received: " + source.getClass().getName());
      }
  
  
      /**
       * Clone a component metadata item, returning a mutable
       * instance.
       *
       * @param source The source metadata item.
       *
       * @return A mutable instance of this metadata item.
       */
      static public ComponentMetadata cloneComponentMetadata(ComponentMetadata source) {
          return (ComponentMetadata) cloneMetadata(source);
      }
  
      /**
       * Clone a target item, returning a mutable
       * instance.
       *
       * @param source The source target item.
       *
       * @return A mutable instance of this target item.
       */
      static public Target cloneTarget(Target source) {
          return (Target) cloneMetadata(source);
      }
  
      /**
       * Create a new metadata instance of the given type
       *
       * @param type the class of the Metadata object to create
       * @param <T>
       * @return a new instance
       */
      public static <T extends Metadata> T createMetadata(Class<T> type) {
          if (MapMetadata.class.isAssignableFrom(type)) {
              return type.cast(new MapMetadataImpl());
          } else if (NullMetadata.class.isAssignableFrom(type)) {
              return type.cast(NullMetadata.NULL);
          } else if (PropsMetadata.class.isAssignableFrom(type)) {
              return type.cast(new PropsMetadataImpl());
          } else if (RefMetadata.class.isAssignableFrom(type)) {
              return type.cast(new RefMetadataImpl());
          } else if (IdRefMetadata.class.isAssignableFrom(type)) {
              return type.cast(new IdRefMetadataImpl());
          } else if (ValueMetadata.class.isAssignableFrom(type)) {
              return type.cast(new ValueMetadataImpl());
          } else if (BeanMetadata.class.isAssignableFrom(type)) {
              return type.cast(new BeanMetadataImpl());
          } else if (ReferenceListMetadata.class.isAssignableFrom(type)) {
              return type.cast(new ReferenceListMetadataImpl());
          } else if (ServiceMetadata.class.isAssignableFrom(type)) {
              return type.cast(new ServiceMetadataImpl());
          } else if (ReferenceMetadata.class.isAssignableFrom(type)) {
              return type.cast(new ReferenceMetadataImpl());
          } else if (CollectionMetadata.class.isAssignableFrom(type)) {
              return type.cast(new CollectionMetadataImpl());
          } else if (PassThroughMetadata.class.isAssignableFrom(type)) {
              return type.cast(new PassThroughMetadataImpl());
          } else {
              throw new IllegalArgumentException("Unsupport metadata type: " + (type != null ? type.getName() : null));
          }
      }
      
      public static List<BeanArgument> validateBeanArguments(List<BeanArgument> arguments) {
          if (arguments == null || arguments.isEmpty()) {
              return arguments;
          }
          // check if all or none arguments have index attribute
          boolean hasIndexFirst = (arguments.get(0).getIndex() > -1);
          for (int i = 1; i < arguments.size(); i++) {
              boolean hasIndex = (arguments.get(i).getIndex() > -1);
              if ( (hasIndexFirst && !hasIndex) ||
                   (!hasIndexFirst && hasIndex) ) {
                  throw new IllegalArgumentException("Index attribute must be specified either on all or none constructor arguments");
              }
          }
          if (hasIndexFirst) {
              // sort the arguments
              List<BeanArgument> argumentsCopy = new ArrayList<BeanArgument>(arguments);
              Collections.sort(argumentsCopy, MetadataUtil.BEAN_COMPARATOR);
              arguments = argumentsCopy;
              
              // check if the indexes are sequential
              for (int i = 0; i < arguments.size(); i++) {
                  int index = arguments.get(i).getIndex();
                  if (index > i) {
                      throw new IllegalArgumentException("Missing attribute index");                    
                  } else if (index < i) {
                      throw new IllegalArgumentException("Duplicate attribute index");
                  } // must be the same
              }            
          }
          
          return arguments;
      }
      
      public static boolean isPrototypeScope(BeanMetadata metadata) {
          return (BeanMetadata.SCOPE_PROTOTYPE.equals(metadata.getScope()) || 
                  (metadata.getScope() == null && metadata.getId() == null));
      }
      
      public static boolean isSingletonScope(BeanMetadata metadata) {
          return (BeanMetadata.SCOPE_SINGLETON.equals(metadata.getScope())  ||
                  (metadata.getScope() == null && metadata.getId() != null));
      }
      
      public static boolean isCustomScope(BeanMetadata metadata) {
          return (metadata.getScope() != null &&
                  !BeanMetadata.SCOPE_PROTOTYPE.equals(metadata.getScope()) &&
                  !BeanMetadata.SCOPE_SINGLETON.equals(metadata.getScope()));
      }
      
      private static class BeanArgumentComparator implements Comparator<BeanArgument>, Serializable {
          public int compare(BeanArgument object1, BeanArgument object2) {
              return object1.getIndex() - object2.getIndex();
          }        
      }
         
  }
  
  