View Javadoc

1   /*
2   
3       dsh-venn-tools  Command line tools for venn diagrams.
4       Copyright (c) 2010-2013 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.venn.tools;
25  
26  import java.io.BufferedWriter;
27  import java.io.File;
28  import java.io.FileWriter;
29  import java.io.IOException;
30  import java.io.OutputStreamWriter;
31  import java.io.PrintWriter;
32  
33  import java.util.ArrayList;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  
39  import com.google.common.collect.ImmutableMap;
40  import com.google.common.collect.ImmutableSet;
41  import com.google.common.collect.Sets;
42  
43  import org.dishevelled.bitset.MutableBitSet;
44  import org.dishevelled.bitset.ImmutableBitSet;
45  
46  import org.dishevelled.commandline.ArgumentList;
47  import org.dishevelled.commandline.CommandLine;
48  import org.dishevelled.commandline.CommandLineParseException;
49  import org.dishevelled.commandline.CommandLineParser;
50  import org.dishevelled.commandline.Switch;
51  import org.dishevelled.commandline.Usage;
52  
53  import org.dishevelled.commandline.argument.FileArgument;
54  
55  import org.dishevelled.venn.VennModel;
56  
57  import org.dishevelled.venn.model.VennModels;
58  
59  /**
60   * Venn an arbitrary number of input files.
61   *
62   * @author  Michael Heuer
63   */
64  public final class Venn
65      extends AbstractVennRunnable
66  {
67      /** List of input files. */
68      private final List<File> inputFiles;
69  
70      /** Usage string. */
71      private static final String USAGE = "java Venn [args] [input-files]";
72  
73  
74      /**
75       * Create a new venn with the specified arguments.
76       *
77       * @param count true to output count(s) only
78       * @param header true to ouput header(s)
79       * @param inputFiles list of input files, must not be null, must contain at least
80       *    two input files, and must not contain more than 29 input files
81       */
82      private Venn(final boolean count, final boolean header, final List<File> inputFiles)
83      {
84          super(count, header);
85  
86          if (inputFiles == null)
87          {
88              throw new IllegalArgumentException("inputFiles must not be null");
89          }
90          if (inputFiles.size() < 2)
91          {
92              throw new IllegalArgumentException("inputFiles must contain at least two input files");
93          }
94          if (inputFiles.size() > 29)
95          {
96              throw new IllegalArgumentException("inputFiles must contain less than 30 input files");
97          }
98          this.inputFiles = inputFiles;
99      }
100 
101 
102     /** {@inheritDoc} */
103     public void run()
104     {
105         int n = inputFiles.size();
106         List<String> labels = new ArrayList<String>(n);
107         List<Set<String>> sets = new ArrayList<Set<String>>(n);
108         for (File inputFile : inputFiles)
109         {
110             labels.add(inputFile.getName());
111             sets.add(read(inputFile));
112         }
113         VennModel<String> model = VennModels.createVennModel(sets);
114 
115         ImmutableMap.Builder<ImmutableBitSet, Set<String>> builder = ImmutableMap.builder();
116         Set<Set<Integer>> powerSet = Sets.powerSet(range(n));
117         for (Set<Integer> set : powerSet)
118         {
119             if (!set.isEmpty())
120             {
121                 ImmutableBitSet key = toImmutableBitSet(set);
122                 builder.put(key, model.exclusiveTo(first(key), additional(key)));
123             }
124         }
125         Map<ImmutableBitSet, Set<String>> views = builder.build();
126 
127         PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
128         if (header())
129         {
130             for (Map.Entry<ImmutableBitSet, Set<String>> entry : views.entrySet())
131             {
132                 write(true, buildLabel(labels, entry.getKey()), writer);
133             }
134             // todo trim extra \t
135             writer.print("\n");
136         }
137 
138         if (count())
139         {
140             for (Map.Entry<ImmutableBitSet, Set<String>> entry : views.entrySet())
141             {
142                 write(true, entry.getValue().size(), writer);
143             }
144             // todo trim extra \t
145             writer.print("\n");
146         }
147         else
148         {
149             List<Iterator<String>> iterators = new ArrayList<Iterator<String>>();
150 
151             for (Map.Entry<ImmutableBitSet, Set<String>> entry : views.entrySet())
152             {
153                 iterators.add(entry.getValue().iterator());
154             }
155 
156             boolean done = false;
157             while (!done)
158             {
159                 boolean left = false;
160                 for (Iterator<String> iterator : iterators)
161                 {
162                     if (iterator.hasNext())
163                     {
164                         writer.write(iterator.next());
165                         left = true;
166                     }
167                     writer.write("\t");
168                 }
169                 // todo trim extra \t
170                 writer.print("\n");
171                 done = !left;
172             }
173         }
174         try
175         {
176             writer.close();
177         }
178         catch (Exception e)
179         {
180             // ignore
181         }
182     }
183 
184     // copied from VennNode.java
185 
186     static String buildLabel(final List<String> labels, final ImmutableBitSet key)
187     {
188         int first = first(key);
189         int[] additional = additional(key);
190         StringBuilder sb = new StringBuilder();
191         sb.append(labels.get(first));
192         if (additional.length > 0) {
193             for (int i = 0, size = additional.length - 1; i < size; i++)
194             {
195                 sb.append(", ");
196                 sb.append(labels.get(additional[i]));
197             }
198             sb.append(" and ");
199             sb.append(labels.get(additional[Math.max(0, additional.length - 1)]));
200         }
201         sb.append(" only");
202         return sb.toString();
203     }
204 
205     static ImmutableSet<Integer> range(final int n)
206     {
207         Set<Integer> range = Sets.newHashSet();
208         for (int i = 0; i < n; i++)
209         {
210             range.add(Integer.valueOf(i));
211         }
212         return ImmutableSet.copyOf(range);
213     }
214 
215     static int first(final ImmutableBitSet bitSet)
216     {
217         return (int) bitSet.nextSetBit(0L);
218     }
219 
220     static int[] additional(final ImmutableBitSet bitSet)
221     {
222         int[] additional = new int[Math.max(0, (int) bitSet.cardinality() - 1)];
223         int index = 0;
224         long first = bitSet.nextSetBit(0);
225         for (long value = bitSet.nextSetBit(first + 1); value >= 0L; value = bitSet.nextSetBit(value + 1))
226         {
227             additional[index] = (int) value;
228             index++;
229         }
230         return additional;
231     }
232 
233     static int first(final Set<Integer> values)
234     {
235         if (values.isEmpty())
236         {
237             return -1;
238         }
239         return values.iterator().next().intValue();
240     }
241 
242     static int[] additional(final Set<Integer> values)
243     {
244         int[] additional = new int[Math.max(0, values.size() - 1)];
245         int index = -1;
246         for (Integer value : values)
247         {
248             if (index >= 0)
249             {
250                 additional[index] = value.intValue();
251             }
252             index++;
253         }
254         return additional;
255     }
256 
257     static ImmutableBitSet toImmutableBitSet(final Set<Integer> indices)
258     {
259         if (indices == null)
260         {
261             throw new IllegalArgumentException("indices must not be null");
262         }
263         if (indices.isEmpty())
264         {
265             throw new IllegalArgumentException("indices must not be empty");
266         }
267         MutableBitSet mutableBitSet = new MutableBitSet(indices.size());
268         for (Integer index : indices)
269         {
270             mutableBitSet.set(index);
271         }
272         mutableBitSet.trimTrailingZeros();
273         return mutableBitSet.immutableCopy();
274     }
275 
276     /**
277      * Main.
278      *
279      * @param args command line arguments
280      */
281     public static void main(final String[] args)
282     {
283         CommandLine commandLine = null;
284         ArgumentList arguments = null;
285         try
286         {
287             Switch help = new Switch("h", "help", "display help message");
288             Switch count = new Switch("c", "count", "output count(s) only");
289             Switch header = new Switch("e", "header", "output header(s)");
290 
291             arguments = new ArgumentList(help, count, header);
292             commandLine = new CommandLine(args);
293             CommandLineParser.parse(commandLine, arguments);
294 
295             if (help.wasFound())
296             {
297                 Usage.usage(USAGE, null, commandLine, arguments, System.out);
298             }
299             else
300             {
301                 List<File> inputFiles = new ArrayList<File>(args.length);
302                 for (int i = 0; i < args.length; i++)
303                 {
304                     // ick.
305                     if (!args[i].startsWith("-"))
306                     {
307                         inputFiles.add(new File(args[i]));
308                     }
309                 }
310                 new Venn(count.wasFound(), header.wasFound(), inputFiles).run();
311             }
312         }
313         catch (CommandLineParseException e)
314         {
315             Usage.usage(USAGE, e, commandLine, arguments, System.err);
316         }
317         catch (IllegalArgumentException e)
318         {
319             Usage.usage(USAGE, e, commandLine, arguments, System.err);
320         }
321     }
322 }