001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic.issue.lbcore224;
015
016import java.io.*;
017import java.util.ArrayList;
018import java.util.List;
019import java.util.regex.Matcher;
020import java.util.regex.Pattern;
021
022/**
023 * Reduce a file consisting of lock and unlock operations by removing matching lock/unlocks.
024 */
025public class Reduce {
026
027    static int NA = -1;
028
029    enum OperationType {
030        LOCK, UNLOCK
031    }
032
033    public static void main(String[] args) throws IOException {
034        File inputFile = new File(args[0]);
035        if (!inputFile.exists()) {
036            throw new IllegalArgumentException("Missing file [" + args[0] + "]");
037        }
038        List<String> lines = readFile(inputFile);
039        System.out.println("Lines count=" + lines.size());
040        List<Structure> structuredLines = structure(lines);
041        List<Structure> reduction = reduce(structuredLines);
042        if (reduction.isEmpty()) {
043            System.out.println("Reduction is EMPTY as it should be.");
044        } else {
045            System.out.println("Non-empty reduction!!! WTF?");
046            System.out.println(reduction);
047        }
048
049    }
050
051    private static List<String> readFile(File inputFile) throws IOException {
052        BufferedReader reader = null;
053        List<String> lines = new ArrayList<>();
054        try {
055            reader = new BufferedReader(new FileReader(inputFile));
056            String line;
057            while ((line = reader.readLine()) != null) {
058                lines.add(line);
059            }
060        } finally {
061            if (reader != null)
062                try {
063                    reader.close();
064                } catch (IOException e) {
065                }
066        }
067        return lines;
068    }
069
070    private static List<Structure> reduce(List<Structure> structuredLines) {
071        List<Structure> matching = new ArrayList<>();
072        int lockIndex = 0;
073        while (lockIndex < structuredLines.size()) {
074            lockIndex = findNearestLock(structuredLines, lockIndex);
075            if (lockIndex == NA)
076                break;
077            else {
078                int unlockIndex = findNearestUnlockInSameThread(structuredLines, lockIndex);
079                if (unlockIndex != NA) {
080                    matching.add(structuredLines.get(lockIndex));
081                    matching.add(structuredLines.get(unlockIndex));
082                }
083                lockIndex++;
084            }
085        }
086        System.out.println("matching list size: " + matching.size());
087        List<Structure> reduction = new ArrayList<Structure>();
088        for (Structure s : structuredLines) {
089            if (!matching.contains(s)) {
090                reduction.add(s);
091            }
092        }
093        return reduction;
094
095    }
096
097    private static int findNearestLock(List<Structure> reduction, int index) {
098        for (int i = index; i < reduction.size(); i++) {
099            Structure s = reduction.get(i);
100            if (s.operationType == OperationType.LOCK) {
101                return i;
102            }
103        }
104        return NA;
105    }
106
107    private static int findNearestUnlockInSameThread(List<Structure> reduction, int lockIndex) {
108        int firstCandidateIndex = lockIndex + 1;
109        Structure lockStructure = reduction.get(lockIndex);
110        for (int i = firstCandidateIndex; i < reduction.size(); i++) {
111            Structure s = reduction.get(i);
112            if (s.operationType == OperationType.UNLOCK && lockStructure.thread.equals(s.thread)) {
113                return i;
114            }
115        }
116        return NA;
117    }
118
119    static List<Structure> structure(List<String> lines) {
120        List<Structure> structuredLines = new ArrayList<>();
121        Pattern p = Pattern.compile("(\\d{2,5})\\ +(.*) (LOCK|UNLOCK)");
122
123        for (String line : lines) {
124            Matcher m = p.matcher(line);
125            if (m.matches()) {
126                String relTime = m.group(1);
127                String t = m.group(2);
128                String opStr = m.group(3);
129                Structure structure = buildStructure(relTime, t, opStr);
130                structuredLines.add(structure);
131            } else {
132                System.out.println("NON MATCHING LINE: [" + line + "]");
133            }
134
135        }
136        return structuredLines;
137    }
138
139    private static Structure buildStructure(String relTime, String t, String opStr) {
140        long r = Long.parseLong(relTime);
141        OperationType operationType;
142        if (opStr.equals("LOCK"))
143            operationType = OperationType.LOCK;
144        else if (opStr.equals("UNLOCK")) {
145            operationType = OperationType.UNLOCK;
146        } else {
147            throw new IllegalArgumentException(opStr + " is not LOCK|UNLOCK");
148        }
149        return new Structure(r, t, operationType);
150    }
151
152    static class Structure {
153        long time;
154        String thread;
155        OperationType operationType;
156
157        Structure(long time, String thread, OperationType operationType) {
158            this.time = time;
159            this.thread = thread;
160            this.operationType = operationType;
161        }
162
163        @Override
164        public String toString() {
165            return "Structure{" + "time=" + time + ", thread='" + thread + '\'' + ", operationType=" + operationType + '}';
166        }
167    }
168
169}