/**
 * 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.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.camel.CamelContext;
import org.apache.camel.Component;
import org.apache.camel.EndpointConfiguration;
import org.apache.camel.TypeConverter;
import org.apache.camel.util.UnsafeUriCharactersEncoder;

/**
 * Fallback implementation of {@link EndpointConfiguration} used by {@link Component}s
 * that did not yet define a configuration type.
 */
public final class MappedEndpointConfiguration extends DefaultEndpointConfiguration {
    // TODO: need 2 sets to differentiate between user keys and fixed keys
    private Map<String, Object> params = new LinkedHashMap<String, Object>();

    MappedEndpointConfiguration(CamelContext camelContext) {
        super(camelContext);
    }

    MappedEndpointConfiguration(CamelContext camelContext, String uri) {
        super(camelContext);
        setURI(uri);
    }

    @SuppressWarnings("unchecked")
    public <T> T getParameter(String name) {
        return (T) params.get(name);
    }

    @Override
    public <T> void setParameter(String name, T value) {
        params.put(name, value);
    }
    
    @Override
    public boolean equals(Object other) {
        // if all parameters including scheme are the same, the component and uri must be the same too
        boolean eq = params.equals(((MappedEndpointConfiguration)other).params);
        return this == other || (this.getClass() == other.getClass() && eq);
    }

    @Override
    public int hashCode() {
        return params.hashCode();
    }

    @Override
    protected void parseURI() {
        ConfigurationHelper.populateFromURI(getCamelContext(), this, new ConfigurationHelper.ParameterSetter() {
            @Override
            public <T> void set(CamelContext camelContext, EndpointConfiguration config, String name, T value) {
                if (name != null && value != null) {
                    params.put(name, value);
                }
            }
        });
    }

    @Override
    public String toUriString(UriFormat format) {
        Set<Map.Entry<String, Object>> entries = params.entrySet();
        List<String> queryParams = new ArrayList<String>();
        
        String scheme = null;
        String schemeSpecificPart = null;
        String authority = null;
        String path = null;
        String fragment = null;

        TypeConverter converter = getCamelContext().getTypeConverter();

        // Separate URI values from query parameters
        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (key.equals(EndpointConfiguration.URI_SCHEME)) {
                scheme = converter.convertTo(String.class, value);
            } else if (key.equals(EndpointConfiguration.URI_SCHEME_SPECIFIC_PART)) {
                schemeSpecificPart = converter.convertTo(String.class, value);
            } else if (key.equals(EndpointConfiguration.URI_AUTHORITY)) {
                authority = converter.convertTo(String.class, value);
            } else if (key.equals(EndpointConfiguration.URI_USER_INFO)) {
                // ignore, part of authority
            } else if (key.equals(EndpointConfiguration.URI_HOST)) {
                // ignore, part of authority
            } else if (key.equals(EndpointConfiguration.URI_PORT)) {
                // ignore, part of authority
            } else if (key.equals(EndpointConfiguration.URI_PATH)) {
                path = converter.convertTo(String.class, value);
            } else if (key.equals(EndpointConfiguration.URI_QUERY)) {
                // ignore, but this should not be the case, may be a good idea to log...
            } else if (key.equals(EndpointConfiguration.URI_FRAGMENT)) {
                fragment = converter.convertTo(String.class, value);
            } else {
                // convert to "param=value" format here, order will be preserved
                if (value instanceof List) {
                    for (Object item : (List<?>)value) {
                        queryParams.add(key + "=" + UnsafeUriCharactersEncoder.encode(item.toString()));
                    }
                } else {
                    queryParams.add(key + "=" + UnsafeUriCharactersEncoder.encode(value.toString()));
                }
            }
        }

        Collections.sort(queryParams);
        String q = "";
        for (String entry : queryParams) {
            q += q.length() == 0 ? "" : "&";
            q += entry;
        }

        StringBuffer u = new StringBuffer(64);
        if (scheme != null) {
            u.append(scheme); // SHOULD NOT be null
            u.append(":");
        }
        if (authority != null) {
            u.append("//");
            u.append(authority);
            u.append(path);
            if (q.length() > 0) {
                u.append("?");
                u.append(q);
            }
            if (fragment != null) {
                u.append("#");
                u.append(fragment);
            }
        } else {
            // add leading // if not provided
            if (!schemeSpecificPart.startsWith("//")) {
                u.append("//");
            }
            u.append(schemeSpecificPart);
        }
        return u.toString();
    }
}

@@ -2106,24 +2106,140 @@
 ct other) %7B%0A
+        if (other == null %7C%7C !(other instanceof MappedEndpointConfiguration)) %7B%0A            return false;%0A        %7D%0A
         // i
@@ -2333,89 +2333,8 @@
 too%0A
-        boolean eq = params.equals(((MappedEndpointConfiguration)other).params);%0A
     
@@ -2401,18 +2401,74 @@
 ss() && 
-eq
+params.equals(((MappedEndpointConfiguration)other).params)
 );%0A    %7D

Fix equals() to account for null and other Object types