/*******************************************************************************
 * Copyright (c) 2008, 2014 Stuart McCulloch
 * 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
 *
 * Contributors:
 *    Stuart McCulloch - initial API and implementation
 *******************************************************************************/

package org.eclipse.sisu.peaberry.util;

import static java.util.Collections.singletonMap;
import static java.util.Collections.unmodifiableMap;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;

/**
 * Methods for dealing with service attributes.
 * 
 * @author mcculls@gmail.com (Stuart McCulloch)
 */
public final class Attributes {

  // instances not allowed
  private Attributes() {}

  /**
   * Convert the given service properties into a service attribute map.
   * 
   * @param properties service properties
   * @return service attributes
   * 
   * @throws IllegalArgumentException if there are any non-String keys
   */
  public static Map<String, ?> properties(final Properties properties) {
    final Map<String, Object> attributes = new HashMap<String, Object>(2 * properties.size());

    /*
     * Sigh, Properties is a really messed-up class... in Java5 there is only
     * one method that returns all the keys _including default keys_ and that
     * throws a ClassCastException if there happen to be any non-String keys.
     * (Java6 adds stringPropertyNames, but we're currently targeting Java5)
     */
    try {
      for (final Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) {
        final String key = (String) e.nextElement();
        attributes.put(key, properties.getProperty(key));
      }
    } catch (final ClassCastException e) {
      throw new IllegalArgumentException("Property map contains non-String key", e);
    }

    // now add non-String values that have String keys
    for (final Entry<?, ?> entry : properties.entrySet()) {
      attributes.put(entry.getKey().toString(), entry.getValue());
    }

    return unmodifiableMap(attributes);
  }

  /**
   * Convert the given LDAP distinguished names into a service attribute map.
   * 
   * @param names distinguished names
   * @return service attributes
   * 
   * @throws IllegalArgumentException if there are any invalid names
   * 
   * @see <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC-2253</a>
   */
  public static Map<String, ?> names(final String... names) {
    final Map<String, Object> attributes = new HashMap<String, Object>(2 * names.length);

    for (final String n : names) {
      try {
        for (final Rdn rdn : new LdapName(n).getRdns()) { // NOPMD
          attributes.put(rdn.getType(), rdn.getValue());
        }
      } catch (final InvalidNameException e) {
        throw new IllegalArgumentException("Bad LDAP name: " + n, e);
      }
    }

    return unmodifiableMap(attributes);
  }

  /**
   * Create an <i>objectClass</i> service attribute from the given API.
   * 
   * @param interfaces service API
   * @return service attributes
   */
  public static Map<String, ?> objectClass(final Class<?>... interfaces) {
    final String[] objectClass = new String[interfaces.length];

    for (int i = 0; i < interfaces.length; i++) {
      objectClass[i] = interfaces[i].getName();
    }

    return singletonMap("objectClass", objectClass);
  }

  /**
   * Create a union of all the services attributes passed into this method.
   * 
   * @param attributes collection of service attributes
   * @return union of the given service attributes
   * 
   * @since 1.1
   */
  @SuppressWarnings("unchecked")
  public static Map<String, ?> union(final Map... attributes) {
    final Map<String, Object> unionMap = new HashMap<String, Object>();

    for (final Map a : attributes) {
      if (null != a) {
        unionMap.putAll(a);
      }
    }

    return unmodifiableMap(unionMap);
  }
}
