// Path: pie-core/src/main/java/com/coverity/security/pie/util/IOUtil.java
// public class IOUtil {
//     public static void closeSilently(Closeable closeable) {
//         if (closeable != null) {
//             try {
//                 closeable.close();
//             } catch (IOException e) {
//                 // Do nothing
//             }
//         }
//     }
//     
//     public static String readFile(File file) throws IOException {
//         return toString(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
//     }
//     public static String readFile(String file) throws IOException {
//         return toString(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
//     }
//     public static String readFile(URL file) throws IOException {
//         return toString(file.openStream());
//     }
//     
//     public static String toString(InputStream is) throws IOException {
//         try {
//             return toString(new InputStreamReader(is, StandardCharsets.UTF_8));
//         } finally {
//             closeSilently(is);
//         }
//     }
//     public static String toString(Reader reader) throws IOException {
//         StringBuilder sb = new StringBuilder();
//         try {
//             char buffer[] = new char[4096];
//             int n;
//             while ((n = reader.read(buffer)) > 0) {
//                 sb.append(buffer, 0, n);
//             }
//             reader.close();
//         } catch (IOException e) {
//             closeSilently(reader);
//             throw e;
//         }
//         return sb.toString();
//     }
//     
//     
//     public static void writeFile(File file, String content) throws IOException {
//         writeFile(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8), content);
//     }
//     private static void writeFile(Writer fw, String content) throws IOException {
//         try {
//             fw.write(content);
//             fw.close();
//         } catch (IOException e) {
//             closeSilently(fw);
//             throw e;
//         }
//     }
//     
// }
// 
// Path: pie-core/src/main/java/com/coverity/security/pie/util/PieLogger.java
// public class PieLogger {
//     private static PieConfig pieConfig = null;
//     private static PrintWriter logWriter = null;
// 
//     public static void setPieConfig(PieConfig pieConfig) {
//         PieLogger.pieConfig = pieConfig;
//         if (logWriter != null) {
//             IOUtil.closeSilently(logWriter);
//             logWriter = null;
//         }
//         if (pieConfig != null && pieConfig.isLoggingEnabled()) {
//             try {
//                 logWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(pieConfig.getLogPath(), true), StandardCharsets.UTF_8));
//             } catch (FileNotFoundException e) {
//                 throw new RuntimeException(e);
//             }
//         }
//     }
// 
//     public static enum LogLevel {
//         DEBUG,
//         INFO,
//         WARN,
//         ERROR
//     }
// 
//     public static void log(LogLevel logLevel, String msg, Throwable throwable) {
//         if (logWriter == null) {
//             return;
//         }
//         logWriter.append("[").append(new Date().toString()).append("] ").append(logLevel.toString()).append(": ").append(msg).append("\n");
//         if (throwable != null) {
//             throwable.printStackTrace(logWriter);
//         }
//         logWriter.flush();
//     }
// 
//     public static void log(LogLevel logLevel, String msg) {
//         log(logLevel, msg, null);
//     }
// 
//     public static void debug(String msg) {
//         log(LogLevel.DEBUG, msg);
//     }
//     public static void debug(String msg, Throwable throwable) {
//         log(LogLevel.DEBUG, msg, throwable);
//     }
//     public static void info(String msg) {
//         log(LogLevel.INFO, msg);
//     }
//     public static void info(String msg, Throwable throwable) {
//         log(LogLevel.INFO, msg, throwable);
//     }
//     public static void warn(String msg) {
//         log(LogLevel.WARN, msg);
//     }
//     public static void warn(String msg, Throwable throwable) {
//         log(LogLevel.WARN, msg, throwable);
//     }
//     public static void error(String msg) {
//         log(LogLevel.ERROR, msg);
//     }
//     public static void error(String msg, Throwable throwable) {
//         log(LogLevel.ERROR, msg, throwable);
//     }
// 
// }


import com.coverity.security.pie.util.IOUtil;
import com.coverity.security.pie.util.PieLogger;
import org.json.JSONObject;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
    private FactTreeNode collapseFactTreeNode(String rootValue, Collection<FactTreeNode> children, FactMetaData factMetaData) {
        if (children.size() == 0) {
            return new FactTreeNode(rootValue);
        }
        
        Map<String, Collection<FactTreeNode>> childMap = new HashMap<String, Collection<FactTreeNode>>(children.size());
        for (FactTreeNode child : children) {
            if (!childMap.containsKey(child.value)) {
                childMap.put(child.value, new ArrayList<FactTreeNode>(child.children));
            } else {
                childMap.get(child.value).addAll(child.children);
            }
        }
        childMap = factMetaData.getCollapser(policyConfig).collapse(childMap);
        
        FactTreeNode factTreeNode = new FactTreeNode(rootValue);
        for (Map.Entry<String, Collection<FactTreeNode>> entry : childMap.entrySet()) {
            factTreeNode.children.add(collapseFactTreeNode(
                    entry.getKey(), entry.getValue(), factMetaData.getChildFactMetaData(entry.getKey())));
        }
        return factTreeNode;
    }

    /**
     * Reads a PIE security policy from the reader argument, replacing any current policy data with what is read in.
     *
     * @param reader The reader with the raw policy content.
     * @throws IOException
     */
    public void parsePolicy(Reader reader) throws IOException {
// Path: pie-core/src/main/java/com/coverity/security/pie/util/IOUtil.java
// public class IOUtil {
//     public static void closeSilently(Closeable closeable) {
//         if (closeable != null) {
//             try {
//                 closeable.close();
//             } catch (IOException e) {
//                 // Do nothing
//             }
//         }
//     }
//     
//     public static String readFile(File file) throws IOException {
//         return toString(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
//     }
//     public static String readFile(String file) throws IOException {
//         return toString(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
//     }
//     public static String readFile(URL file) throws IOException {
//         return toString(file.openStream());
//     }
//     
//     public static String toString(InputStream is) throws IOException {
//         try {
//             return toString(new InputStreamReader(is, StandardCharsets.UTF_8));
//         } finally {
//             closeSilently(is);
//         }
//     }
//     public static String toString(Reader reader) throws IOException {
//         StringBuilder sb = new StringBuilder();
//         try {
//             char buffer[] = new char[4096];
//             int n;
//             while ((n = reader.read(buffer)) > 0) {
//                 sb.append(buffer, 0, n);
//             }
//             reader.close();
//         } catch (IOException e) {
//             closeSilently(reader);
//             throw e;
//         }
//         return sb.toString();
//     }
//     
//     
//     public static void writeFile(File file, String content) throws IOException {
//         writeFile(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8), content);
//     }
//     private static void writeFile(Writer fw, String content) throws IOException {
//         try {
//             fw.write(content);
//             fw.close();
//         } catch (IOException e) {
//             closeSilently(fw);
//             throw e;
//         }
//     }
//     
// }
// 
// Path: pie-core/src/main/java/com/coverity/security/pie/util/PieLogger.java
// public class PieLogger {
//     private static PieConfig pieConfig = null;
//     private static PrintWriter logWriter = null;
// 
//     public static void setPieConfig(PieConfig pieConfig) {
//         PieLogger.pieConfig = pieConfig;
//         if (logWriter != null) {
//             IOUtil.closeSilently(logWriter);
//             logWriter = null;
//         }
//         if (pieConfig != null && pieConfig.isLoggingEnabled()) {
//             try {
//                 logWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(pieConfig.getLogPath(), true), StandardCharsets.UTF_8));
//             } catch (FileNotFoundException e) {
//                 throw new RuntimeException(e);
//             }
//         }
//     }
// 
//     public static enum LogLevel {
//         DEBUG,
//         INFO,
//         WARN,
//         ERROR
//     }
// 
//     public static void log(LogLevel logLevel, String msg, Throwable throwable) {
//         if (logWriter == null) {
//             return;
//         }
//         logWriter.append("[").append(new Date().toString()).append("] ").append(logLevel.toString()).append(": ").append(msg).append("\n");
//         if (throwable != null) {
//             throwable.printStackTrace(logWriter);
//         }
//         logWriter.flush();
//     }
// 
//     public static void log(LogLevel logLevel, String msg) {
//         log(logLevel, msg, null);
//     }
// 
//     public static void debug(String msg) {
//         log(LogLevel.DEBUG, msg);
//     }
//     public static void debug(String msg, Throwable throwable) {
//         log(LogLevel.DEBUG, msg, throwable);
//     }
//     public static void info(String msg) {
//         log(LogLevel.INFO, msg);
//     }
//     public static void info(String msg, Throwable throwable) {
//         log(LogLevel.INFO, msg, throwable);
//     }
//     public static void warn(String msg) {
//         log(LogLevel.WARN, msg);
//     }
//     public static void warn(String msg, Throwable throwable) {
//         log(LogLevel.WARN, msg, throwable);
//     }
//     public static void error(String msg) {
//         log(LogLevel.ERROR, msg);
//     }
//     public static void error(String msg, Throwable throwable) {
//         log(LogLevel.ERROR, msg, throwable);
//     }
// 
// }


// Path: pie-core/src/main/java/com/coverity/security/pie/core/Policy.java
import com.coverity.security.pie.util.IOUtil;
import com.coverity.security.pie.util.PieLogger;
import org.json.JSONObject;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

    private FactTreeNode collapseFactTreeNode(String rootValue, Collection<FactTreeNode> children, FactMetaData factMetaData) {
        if (children.size() == 0) {
            return new FactTreeNode(rootValue);
        }
        
        Map<String, Collection<FactTreeNode>> childMap = new HashMap<String, Collection<FactTreeNode>>(children.size());
        for (FactTreeNode child : children) {
            if (!childMap.containsKey(child.value)) {
                childMap.put(child.value, new ArrayList<FactTreeNode>(child.children));
            } else {
                childMap.get(child.value).addAll(child.children);
            }
        }
        childMap = factMetaData.getCollapser(policyConfig).collapse(childMap);
        
        FactTreeNode factTreeNode = new FactTreeNode(rootValue);
        for (Map.Entry<String, Collection<FactTreeNode>> entry : childMap.entrySet()) {
            factTreeNode.children.add(collapseFactTreeNode(
                    entry.getKey(), entry.getValue(), factMetaData.getChildFactMetaData(entry.getKey())));
        }
        return factTreeNode;
    }

    /**
     * Reads a PIE security policy from the reader argument, replacing any current policy data with what is read in.
     *
     * @param reader The reader with the raw policy content.
     * @throws IOException
     */
    public void parsePolicy(Reader reader) throws IOException {
        policyRoot = asFactTreeNode(null, new JSONObject(IOUtil.toString(reader)));