/*
 * #%L
 * Wisdom-Framework
 * %%
 * Copyright (C) 2013 - 2014 Wisdom Framework
 * %%
 * Licensed 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.
 * #L%
 */
package org.wisdom.api.utils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;

/**
 * CookieDataCodec and CookieDataCodecTest are imported from Play Framework.
 * 
 * Enables us to use the same sessions as Play Framework if
 * the secret is the same.
 * 
 * Also really important because we want to make sure that our client
 * side session mechanism is widely used and stable.
 * We don't want to reinvent 
 * the wheel of securely encoding / decoding and signing cookie data.
 * 
 * All praise goes to Play Framework and their awesome work.
 * //TODO this should be a service.
 */
public final class CookieDataCodec {

    private static final String UTF8 = "utf-8";
    
    private CookieDataCodec(){
        //Hide implicite constructor
    }

    /**
     * @param map  the map to decode data into.
     * @param data the data to decode.
     * @throws java.io.UnsupportedEncodingException
     */
    public static void decode(Map<String, String> map, String data) throws UnsupportedEncodingException {
        String[] keyValues = data.split("&");
        for (String keyValue : keyValues) {
            String[] splitted = keyValue.split("=", 2);
            if (splitted.length == 2) {
                map.put(URLDecoder.decode(splitted[0], UTF8), URLDecoder.decode(splitted[1], UTF8));
            }
        }
    }

    /**
     * @param map the data to encode.
     * @return the encoded data.
     * @throws java.io.UnsupportedEncodingException
     */
    public static String encode(Map<String, String> map) throws UnsupportedEncodingException {
        StringBuilder data = new StringBuilder();
        String separator = "";
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (entry.getValue() != null) {
                data.append(separator)
                .append(URLEncoder.encode(entry.getKey(), UTF8))
                .append("=")
                .append(URLEncoder.encode(entry.getValue(), UTF8));
                separator = "&";
            }
        }
        return data.toString();
    }

    /**
     * Constant time for same length String comparison, to prevent timing attacks.
     */
    public static boolean safeEquals(String a, String b) {
        if (a.length() != b.length()) {
            return false;
        } else {
            char equal = 0;
            for (int i = 0; i < a.length(); i++) {
                equal |= a.charAt(i) ^ b.charAt(i);
            }
            return equal == 0;
        }
    }
}

@@ -1273,48 +1273,12 @@
 rk.%0A
- * //TODO this should be a service.%0A
  */%0A
+
 publ
@@ -1363,13 +1363,9 @@
 8%22;%0A
-    
 %0A
+
     
@@ -1391,16 +1391,16 @@
 odec()%7B%0A
+
         
@@ -1414,17 +1414,16 @@
 implicit
-e
  constru
@@ -2328,16 +2328,17 @@
 r = %22%22;%0A
+%0A
         

Refactor the cookie encoder / decoder codec