Guest User

Untitled

a guest
Nov 22nd, 2010
1,011
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.17 KB | None | 0 0
  1. import java.io.*;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4.  
  5. import javax.xml.parsers.DocumentBuilderFactory;
  6.  
  7. import org.w3c.dom.*;
  8.  
  9.  
  10. /**
  11.  * Program to print sequences as defined by XML files provided.
  12.  *
  13.  * Each command line argument is the name of an XML file. Here's an example:
  14.  *
  15.  * <pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  16.  *&lt;limit end="100"&gt;
  17.  *  &lt;combined-sequence&gt;
  18.  *      &lt;base-sequence&gt;
  19.  *          &lt;number-sequence /&gt;
  20.  *      &lt;/base-sequence&gt;
  21.  *      &lt;number-sequence stepSize="3" name="Fizz" /&gt;
  22.  *      &lt;number-sequence stepSize="5" name="Buzz" /&gt;
  23.  *  &lt;/combined-sequence&gt;
  24.  *&lt;/limit&gt;</pre>
  25.  *
  26.  * @author Jeremy
  27.  */
  28. public class PrintSequence {
  29.     /**
  30.      * A generated iterable sequence.
  31.      *
  32.      * IDs are returned in ascending order.
  33.      *
  34.      * Since the sequence is generated, it is immutable--attempts to modify
  35.      * the sequence will throw a runtime exception.
  36.      *
  37.      * @author Jeremy
  38.      */
  39.     private static interface Sequence extends Iterable<Sequence.Entry> {
  40.         /**
  41.          * An id/value pair returned by a Sequence.
  42.          * @author Jeremy
  43.          */
  44.         public static class Entry {
  45.             private final int id;
  46.             private final String name;
  47.            
  48.             /**
  49.              * Constructs an entry with the given ID.
  50.              * @param id ID of the entry to create
  51.              */
  52.             private Entry(final int id) {
  53.                 this.id = id;
  54.                 this.name = null;
  55.             }
  56.            
  57.             /**
  58.              * Constructs an entry with the given ID and name.
  59.              * @param id ID of the entry to create
  60.              * @param name name of the entry to create
  61.              */
  62.             private Entry(final int id, final String name) {
  63.                 this.id = id;
  64.                 this.name = name;
  65.             }
  66.            
  67.             /**
  68.              * Returns the ID of this entry.
  69.              * @return ID of this sequence entry
  70.              */
  71.             public int getId() {
  72.                 return id;
  73.             }
  74.             /**
  75.              * Returns the name of this entry.
  76.              * @return name associated with this entry
  77.              */
  78.             public String getName() {
  79.                 return name == null ? Integer.toString(id) : name;
  80.             }
  81.         }
  82.     }
  83.    
  84.     /**
  85.      * A sequence of multiples of a specified integer, from a specified start
  86.      * point through a specified end point, and with a specified name.
  87.      *
  88.      * @author Jeremy
  89.      */
  90.     private static class NumberSequence implements Sequence {
  91.         private final int start, end, stepSize;
  92.         private final String name;
  93.        
  94.         private class EntryIterator implements Iterator<Entry> {
  95.             private int pos = start - 1;
  96.            
  97.             private EntryIterator() {
  98.                 pos = start - 1;
  99.                 pos -= pos % stepSize;
  100.             }
  101.            
  102.             public boolean hasNext() {
  103.                 return end < 0 || pos + stepSize <= end;
  104.             }
  105.  
  106.             public Sequence.Entry next() {
  107.                 return hasNext() ? new Entry(pos = pos + stepSize, name) : null;
  108.             }
  109.  
  110.             public void remove() {
  111.                 throw new RuntimeException("No.");
  112.             }
  113.         }
  114.        
  115.         /**
  116.          * Constructs a sequence of multiples of stepSize beginning at start
  117.          * and running through end. Each entry's name will be set to the value
  118.          * in name.
  119.          * @param start start point for the sequence
  120.          * @param end end point for the sequence
  121.          * @param stepSize difference between IDs of each consecutive sequence entry
  122.          * @param name name given to each sequence entry
  123.          */
  124.         private NumberSequence(final int start, final int end,
  125.                 final int stepSize, final String name) {
  126.             this.start = start;
  127.             this.end = end;
  128.             this.stepSize = stepSize;
  129.             this.name = name;
  130.         }
  131.        
  132.         /**
  133.          * Constructs a sequence of multiples of stepSize beginning at start
  134.          * and running through end. Each entry's name will be the string form
  135.          * of its ID.
  136.          * @param start start point for the sequence
  137.          * @param end end point for the sequence
  138.          * @param stepSize difference between IDs of each consecutive sequence entry
  139.          */
  140.         private NumberSequence(final int start, final int end,
  141.                 final int stepSize) {
  142.             this.start = start;
  143.             this.end = end;
  144.             this.stepSize = stepSize;
  145.             this.name = null;
  146.         }
  147.        
  148.         /**
  149.          * Constructs a sequence of integers beginning at start and running
  150.          * through end. Each entry's name will be the string form of its ID.
  151.          * @param start start point for the sequence
  152.          * @param end end point for the sequence
  153.          */
  154.         private NumberSequence(final int start, final int end) {
  155.             this.start = start;
  156.             this.end = end;
  157.             this.stepSize = 1;
  158.             this.name = null;
  159.         }
  160.        
  161.         public Iterator<Entry> iterator() {
  162.             return new EntryIterator();
  163.         }
  164.     }
  165.    
  166.     /**
  167.      * A sequence generated by combining other sequences.
  168.      *
  169.      * This will return elements with IDs generated from a given base
  170.      * sequence. If any entries in the other specified sequences have a
  171.      * matching ID, their names will be concatenated to generate the returned
  172.      * entry's name. Otherwise, the returned entry will use the name given by
  173.      * the base sequence.
  174.      * @author Jeremy
  175.      */
  176.     private static class CombinedSequence implements Sequence {
  177.         private final Sequence baseSequence;
  178.         private final Sequence[] otherSequences;
  179.        
  180.         private class EntryIterator implements Iterator<Entry> {
  181.             Iterator<Entry> pos = baseSequence.iterator();
  182.             final Iterator<Entry>[] otherPos =
  183.                 new Iterator[otherSequences.length];
  184.             final Entry[] otherEntries =
  185.                 new Entry[otherSequences.length];
  186.            
  187.             private EntryIterator() {
  188.                 for(int i=0; i<otherPos.length; ++i) {
  189.                     otherPos[i] = otherSequences[i].iterator();
  190.                     otherEntries[i] = otherPos[i].next();
  191.                 }
  192.             }
  193.            
  194.             public boolean hasNext() {
  195.                 return pos.hasNext();
  196.             }
  197.  
  198.             public Entry next() {
  199.                 if(pos.hasNext()) {
  200.                     final Entry baseEnt = pos.next();
  201.                     String name = "";
  202.                     for(int i=0; i<otherPos.length; ++i) {
  203.                         while(baseEnt.getId() > otherEntries[i].getId() && otherPos[i].hasNext())
  204.                             otherEntries[i] = otherPos[i].next();
  205.                         if(baseEnt.getId() == otherEntries[i].getId())
  206.                             name += otherEntries[i].getName();
  207.                     }
  208.                     return new Entry(baseEnt.getId(), name.length() == 0 ?
  209.                             baseEnt.getName() : name);
  210.                 }
  211.                 return null;
  212.             }
  213.  
  214.             public void remove() {
  215.                 throw new RuntimeException("No.");
  216.             }
  217.         }
  218.        
  219.         /**
  220.          * Constructs a sequence generated by combining baseSequence and
  221.          * otherSequence. The returned instance will use IDs from baseSequence
  222.          * and names from otherSequences, falling back to the name from
  223.          * baseSequence when none of the sequences in otherSequences contain
  224.          * matching IDs.
  225.          * @param baseSequence base sequence
  226.          * @param otherSequences other sequences to use to generate names
  227.          */
  228.         private CombinedSequence(final Sequence baseSequence,
  229.                 final Sequence... otherSequences) {
  230.             this.baseSequence = baseSequence;
  231.             this.otherSequences = otherSequences;
  232.         }
  233.        
  234.         public Iterator<Entry> iterator() {
  235.             return new EntryIterator();
  236.         }
  237.     }
  238.    
  239.     /**
  240.      * A sequence terminating on or before the specified endpoint.
  241.      *
  242.      * This generates an input sequence as its output, except that this
  243.      * truncates the sequence as specified.
  244.      *
  245.      * @author Jeremy
  246.      */
  247.     private static class LimitedSequence implements Sequence {
  248.         private final Sequence sequence;
  249.         private final int end;
  250.        
  251.         private class EntryIterator implements Iterator<Entry> {
  252.             private Iterator<Entry> pos = sequence.iterator();
  253.             private Entry lastEntry = pos.next();
  254.  
  255.             public boolean hasNext() {
  256.                 return lastEntry != null && lastEntry.getId() <= end;
  257.             }
  258.  
  259.             public Entry next() {
  260.                 if(hasNext()) {
  261.                     final Entry rv = lastEntry;
  262.                     lastEntry = pos.next();
  263.                     return rv;
  264.                 }
  265.                 return null;
  266.             }
  267.  
  268.             public void remove() {
  269.                 pos.remove();
  270.             }
  271.         }
  272.        
  273.         private LimitedSequence(final Sequence sequence, final int end) {
  274.             this.sequence = sequence;
  275.             this.end = end;
  276.         }
  277.  
  278.         public Iterator<Entry> iterator() {
  279.             return new EntryIterator();
  280.         }
  281.     }
  282.    
  283.     private static class AttributeList {
  284.         private final NamedNodeMap attributes;
  285.        
  286.         private AttributeList(final NamedNodeMap attributes) {
  287.             this.attributes = attributes;
  288.         }
  289.        
  290.         private boolean has(final String key) {
  291.             return attributes.getNamedItem(key) != null;
  292.         }
  293.        
  294.         private String getString(final String key) {
  295.             try {
  296.                 return attributes.getNamedItem(key).getTextContent();
  297.             } catch (Exception e) {
  298.                 throw new IllegalArgumentException(String.format(
  299.                         "Required attribute \"%s\" not found.", key), e);
  300.             }
  301.         }
  302.        
  303.         private int getInteger(final String key) {
  304.             try {
  305.                 return Integer.parseInt(getString(key));
  306.             } catch (NumberFormatException e) {
  307.                 throw new IllegalArgumentException(String.format(
  308.                         "Attribute \"%s\" must have an integer value.", key), e);
  309.             }
  310.         }
  311.     }
  312.    
  313.     /**
  314.      * Generates a sequence from the provided XML DOM
  315.      * @param doc XML document from which to generate a sequence
  316.      * @return sequence generated from XML element provided
  317.      */
  318.     private static Sequence loadSequence(final Node element) {
  319.         final AttributeList attributes =
  320.             new AttributeList(element.getAttributes());
  321.         if(element.getNodeName().equals("number-sequence") ||
  322.                 element.getNodeName().equals("sequence-of-multiples")) {
  323.             return new NumberSequence(
  324.                     attributes.has("start") ? attributes.getInteger("start") : 1,
  325.                     attributes.has("end") ? attributes.getInteger("end") : -1,
  326.                     attributes.has("stepSize") ? attributes.getInteger("stepSize") : 1,
  327.                     attributes.has("name") ? attributes.getString("name") : null);
  328.         } else if(element.getNodeName().equals("combined-sequence")) {
  329.             Sequence baseSequence = null;
  330.             ArrayList<Sequence> otherSequences = new ArrayList<Sequence>();
  331.             final NodeList children = element.getChildNodes();
  332.             for(int i=0; i < children.getLength(); ++i) {
  333.                 final Node child = children.item(i);
  334.                 if(child.getNodeName().equals("base-sequence")) {
  335.                     final NodeList baseChildren = child.getChildNodes();
  336.                     for(int j=0; j < baseChildren.getLength(); ++j) {
  337.                         if(baseChildren.item(j).getNodeType() == Node.ELEMENT_NODE)
  338.                             baseSequence = loadSequence(baseChildren.item(i));
  339.                     }
  340.                 } else if(child.getNodeType() == Node.ELEMENT_NODE) {
  341.                     otherSequences.add(loadSequence(child));
  342.                 }
  343.             }
  344.             if(baseSequence == null)
  345.                 throw new IllegalArgumentException("\"combined-sequence\" requires base sequence!");
  346.             final Sequence[] otherArr = new Sequence[otherSequences.size()];
  347.             return new CombinedSequence(baseSequence,
  348.                     otherSequences.toArray(otherArr));
  349.         } else if(element.getNodeName() == "limit") {
  350.             final NodeList children = element.getChildNodes();
  351.             Sequence innerSequence = null;
  352.             for(int i=0; i < children.getLength(); ++i) {
  353.                 final Node child = children.item(i);
  354.                 if(child.getNodeType() == Node.ELEMENT_NODE)
  355.                     innerSequence = loadSequence(child);
  356.             }
  357.             if(innerSequence == null)
  358.                 throw new IllegalArgumentException("\"limit\" requires inner sequence!");
  359.             return new LimitedSequence(innerSequence, attributes.getInteger("end"));
  360.         }
  361.         throw new IllegalArgumentException(String.format("Unknown XML element \"%s\" in config.", element.getNodeName()));
  362.     }
  363.    
  364.     /**
  365.      * Generates a sequence from the provided XML input stream
  366.      * @param xml XML description of sequence to generate
  367.      * @return sequence generated from XML provided
  368.      */
  369.     private static Sequence loadSequence(final InputStream xml) {
  370.         try {
  371.             return loadSequence(DocumentBuilderFactory.newInstance()
  372.                     .newDocumentBuilder().parse(xml).getDocumentElement());
  373.         } catch (Exception e) {
  374.             e.printStackTrace();
  375.         }
  376.         return null;
  377.     }
  378.    
  379.     /**
  380.      * Generates a sequence from the provided XML file
  381.      * @param xml XML description of sequence to generate
  382.      * @return sequence generated from XML provided
  383.      */
  384.     private static Sequence loadSequence(final File xml) {
  385.         try {
  386.             return loadSequence(new FileInputStream(xml));
  387.         } catch (Exception e) {
  388.             e.printStackTrace();
  389.         }
  390.         return null;
  391.     }
  392.    
  393.     /**
  394.      * Writes the provided sequence to System.out
  395.      * @param sequence sequence to print
  396.      */
  397.     private static void printSequence(final Sequence sequence) {
  398.         final StringBuilder buffer = new StringBuilder();
  399.         for(final Sequence.Entry entry: sequence) {
  400.             buffer.append(entry.getName());
  401.             buffer.append('\n');
  402.         }
  403.         System.out.print(buffer);
  404.     }
  405.    
  406.     public static void main(final String[] args) {
  407.         for(final String arg: args) {
  408.             printSequence(loadSequence(new File(arg)));
  409.         }
  410.     }
  411. }
Advertisement
Add Comment
Please, Sign In to add comment