// Path: pie-core/src/main/java/com/coverity/security/pie/core/StringCollapser.java
// public interface StringCollapser {
//     /**
//      * Collapses a security policy according to the concrete implementation's rules. It takes a map from facts to their
//      * children, and returns a collapsed version of that map. For example, given the map:
//      * {
//      *   A => {'X', 'Y'},
//      *   B => {'Z'}
//      * }
//      *
//      * If the implementation determines that the facts A and B should be collapsed to fact C, then this would return
//      * {
//      *   C => {'X', 'Y', 'Z'}
//      * }
//      *
//      * @param input A map from facts to a collection of child facts.
//      * @param <T> The type of the child facts; irrelevant to the internal procedure of collapsing.
//      * @return The collapsed map of facts to their children.
//      */
//     public <T> Map<String, Collection<T>> collapse(Map<String, Collection<T>> input);
// }
// 
// Path: pie-core/src/main/java/com/coverity/security/pie/util/StringUtil.java
// public class StringUtil {
//     public static String join(String s, String[] arr, int off, int end) {
//         if (off < 0 || end > arr.length) {
//             throw new IllegalArgumentException("Invalid indices");
//         }
//         if (end <= off) {
//             return "";
//         }
//         
//         StringBuilder sb = new StringBuilder();
//         for (int i = off; i < end-1; i++) {
//             sb.append(arr[i]).append(s);
//         }
//         sb.append(arr[end-1]);
//         return sb.toString();
//     }
//     
//     public static String join(String s, List<String> strings) {
//         if (strings.size() == 0) {
//             return "";
//         }
//         StringBuilder sb = new StringBuilder();
//         for (int i = 0; i < strings.size()-1; i++) {
//             sb.append(strings.get(i)).append(s);
//         }
//         sb.append(strings.get(strings.size()-1));
//         return sb.toString();
//     }
//     
// 
//     public static String join(String s, String ... strings) {
//         if (strings.length == 0) {
//             return "";
//         }
//         StringBuilder sb = new StringBuilder();
//         for (int i = 0; i < strings.length-1; i++) {
//             sb.append(strings[i]).append(s);
//         }
//         sb.append(strings[strings.length-1]);
//         return sb.toString();
//     }
// }


import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.coverity.security.pie.core.StringCollapser;
import com.coverity.security.pie.util.StringUtil;
package com.coverity.security.pie.util.collapser;



/**
 * An implementation of the AbstractPathCollapser which collapses paths according to ant-style path matching; i.e.
 * /a/b/* matches everything in the /a/b directory, whereas /a/b/- matches all descendents of /a/b
 */
public class FilePathCollapser extends AbstractPathCollapser implements StringCollapser {

    public FilePathCollapser(int collapseThreshold) {
        super("*", "-", collapseThreshold, 0);
    }

    @Override
    public <T> Map<String, Collection<T>> collapse(Map<String, Collection<T>> input) {
        Map<PathName, Collection<T>> inputMap = new HashMap<PathName, Collection<T>>(input.size());
        for (Map.Entry<String, Collection<T>> fileEntry : input.entrySet()) {
            inputMap.put(new PathName(fileEntry.getKey().split("/"), null), fileEntry.getValue());
        }
        Map<PathName, Collection<T>> outputMap = collapsePaths(inputMap);
        Map<String, Collection<T>> output = new HashMap<String, Collection<T>>(outputMap.size());
        for (Map.Entry<PathName, Collection<T>> pathEntry : outputMap.entrySet()) {
// Path: pie-core/src/main/java/com/coverity/security/pie/core/StringCollapser.java
// public interface StringCollapser {
//     /**
//      * Collapses a security policy according to the concrete implementation's rules. It takes a map from facts to their
//      * children, and returns a collapsed version of that map. For example, given the map:
//      * {
//      *   A => {'X', 'Y'},
//      *   B => {'Z'}
//      * }
//      *
//      * If the implementation determines that the facts A and B should be collapsed to fact C, then this would return
//      * {
//      *   C => {'X', 'Y', 'Z'}
//      * }
//      *
//      * @param input A map from facts to a collection of child facts.
//      * @param <T> The type of the child facts; irrelevant to the internal procedure of collapsing.
//      * @return The collapsed map of facts to their children.
//      */
//     public <T> Map<String, Collection<T>> collapse(Map<String, Collection<T>> input);
// }
// 
// Path: pie-core/src/main/java/com/coverity/security/pie/util/StringUtil.java
// public class StringUtil {
//     public static String join(String s, String[] arr, int off, int end) {
//         if (off < 0 || end > arr.length) {
//             throw new IllegalArgumentException("Invalid indices");
//         }
//         if (end <= off) {
//             return "";
//         }
//         
//         StringBuilder sb = new StringBuilder();
//         for (int i = off; i < end-1; i++) {
//             sb.append(arr[i]).append(s);
//         }
//         sb.append(arr[end-1]);
//         return sb.toString();
//     }
//     
//     public static String join(String s, List<String> strings) {
//         if (strings.size() == 0) {
//             return "";
//         }
//         StringBuilder sb = new StringBuilder();
//         for (int i = 0; i < strings.size()-1; i++) {
//             sb.append(strings.get(i)).append(s);
//         }
//         sb.append(strings.get(strings.size()-1));
//         return sb.toString();
//     }
//     
// 
//     public static String join(String s, String ... strings) {
//         if (strings.length == 0) {
//             return "";
//         }
//         StringBuilder sb = new StringBuilder();
//         for (int i = 0; i < strings.length-1; i++) {
//             sb.append(strings[i]).append(s);
//         }
//         sb.append(strings[strings.length-1]);
//         return sb.toString();
//     }
// }


// Path: pie-core/src/main/java/com/coverity/security/pie/util/collapser/FilePathCollapser.java
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.coverity.security.pie.core.StringCollapser;
import com.coverity.security.pie.util.StringUtil;

package com.coverity.security.pie.util.collapser;



/**
 * An implementation of the AbstractPathCollapser which collapses paths according to ant-style path matching; i.e.
 * /a/b/* matches everything in the /a/b directory, whereas /a/b/- matches all descendents of /a/b
 */
public class FilePathCollapser extends AbstractPathCollapser implements StringCollapser {

    public FilePathCollapser(int collapseThreshold) {
        super("*", "-", collapseThreshold, 0);
    }

    @Override
    public <T> Map<String, Collection<T>> collapse(Map<String, Collection<T>> input) {
        Map<PathName, Collection<T>> inputMap = new HashMap<PathName, Collection<T>>(input.size());
        for (Map.Entry<String, Collection<T>> fileEntry : input.entrySet()) {
            inputMap.put(new PathName(fileEntry.getKey().split("/"), null), fileEntry.getValue());
        }
        Map<PathName, Collection<T>> outputMap = collapsePaths(inputMap);
        Map<String, Collection<T>> output = new HashMap<String, Collection<T>>(outputMap.size());
        for (Map.Entry<PathName, Collection<T>> pathEntry : outputMap.entrySet()) {
            output.put(StringUtil.join("/", pathEntry.getKey().getPathComponents()), pathEntry.getValue());