package bigdata.dblp;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import bigdata.dblp.records.Collection;
import bigdata.dblp.records.Publication;

public class Parser {
    private static Set<String> validProperties;

    static {
        validProperties = new HashSet<>();
        validProperties.add("title");
        validProperties.add("year");
        validProperties.add("journal");
        validProperties.add("volume");
        validProperties.add("number");
        validProperties.add("booktitle");
        validProperties.add("crossref");
        validProperties.add("author");
        validProperties.add("cite");
    }

    public static void parse(InputStream dblpInput, Writer publicationOutput, Writer collectionOutput) throws XMLStreamException, IOException {
        // disable default entity resolution limit (64000)
        System.getProperties().setProperty("jdk.xml.entityExpansionLimit", "0");

        XMLInputFactory factory = XMLInputFactory.newFactory();
        factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
        factory.setXMLResolver(new XMLResolver() {
            @Override
            public Object resolveEntity(String publicID, String systemID, String baseURI, String namespace) throws XMLStreamException {
                if(systemID.equals("dblp.dtd")) {
                    return Parser.class.getResourceAsStream("resources/dblp.dtd");
                }
                return null;
            }
        });
        XMLStreamReader reader = factory.createXMLStreamReader(dblpInput);

        Publication currentPublication = null;
        Collection currentCollection = null;
        String currentProperty = null;
        String currentPropertyBuffer = "";

        while(reader.hasNext()) {
            switch(reader.next()) {
                case XMLStreamConstants.START_ELEMENT:
                    String startElement = reader.getLocalName();
                    if(Publication.isValidType(startElement)) {
                        currentPublication = new Publication(startElement);
                        currentPublication.id = reader.getAttributeValue(null, "key");
                    } else if(Collection.isValidType(startElement)) {
                        currentCollection = new Collection(startElement);
                        currentCollection.id = reader.getAttributeValue(null, "key");
                    } else if((currentPublication != null || currentCollection != null)
                                && isValidProperty(startElement)) {
                        currentProperty = startElement;
                    }
                    break;

                case XMLStreamConstants.CHARACTERS:
                    if(currentProperty != null) {
                        currentPropertyBuffer += reader.getText();
                    }
                    break;

                case XMLStreamConstants.END_ELEMENT:
                    String endElement = reader.getLocalName();
                    if(Publication.isValidType(endElement)) {
                        if(currentPublication.isComplete()) {
                            publicationOutput.append(currentPublication.toString());
                            publicationOutput.append('\n');
                        }
                        currentPublication = null;
                    } else if(Collection.isValidType(endElement)) {
                        if(currentCollection.isComplete()) {
                            collectionOutput.append(currentCollection.toString());
                            collectionOutput.append('\n');
                        }
                        currentCollection = null;
                    } else if(endElement.equals(currentProperty)) {
                        if(currentPublication != null) {
                            switch(currentProperty) {
                                case "title":
                                    currentPublication.title = currentPropertyBuffer;
                                    break;
                                case "year":
                                    try {
                                        currentPublication.year = Integer.valueOf(currentPropertyBuffer);
                                    } catch(NumberFormatException e) {} // ignore publications with invalid year
                                    break;
                                case "journal":
                                case "booktitle":
                                    currentPublication.journal = currentPropertyBuffer;
                                    break;
                                case "crossref":
                                    currentPublication.crossref = currentPropertyBuffer;
                                    break;
                                case "volume":
                                    try {
                                        currentPublication.volume = Integer.valueOf(currentPropertyBuffer);
                                    } catch(NumberFormatException e) {} // ignore invalid volume
                                    break;
                                case "number":
                                    try {
                                        currentPublication.issue = Integer.valueOf(currentPropertyBuffer);
                                    } catch(NumberFormatException e) {} // ignore invalid issue
                                    break;
                                case "author":
                                    currentPublication.authors.add(currentPropertyBuffer);
                                    break;
                                case "cite":
                                    if(!"...".equals(currentPropertyBuffer)) {
                                        currentPublication.cites.add(currentPropertyBuffer);
                                    }
                                    break;
                            }
                        } else if(currentCollection != null) {
                            switch(currentProperty) {
                                case "title":
                                    currentCollection.title = currentPropertyBuffer;
                                    break;
                            }
                        }
                        currentProperty = null;
                        currentPropertyBuffer = "";
                    }
                    break;
            }
        }
    }

    protected static boolean isValidProperty(String name) {
        return validProperties.contains(name);
    }

    public static void main(String[] args) {
        String file;

        if(args.length < 1) {
            System.err.println("Usage: java -jar dblp-importer.jar /path/to/dblp.xml");
            System.exit(1);
            return;
        }

        file = args[0];

        InputStream dblpInput;
        Writer publicationOutput;
        Writer collectionOutput;
		try {
			dblpInput = new FileInputStream(file);
	        publicationOutput = new OutputStreamWriter(new FileOutputStream(file + "-pub"), StandardCharsets.UTF_8);
	        collectionOutput = new OutputStreamWriter(new FileOutputStream(file + "-col"), StandardCharsets.UTF_8);
		} catch (FileNotFoundException e) {
			System.err.println("File or Directory not found: " + e);
			System.exit(2);
			return;
		}

        try {
			parse(dblpInput, publicationOutput, collectionOutput);
		} catch (XMLStreamException | IOException e) {
			System.err.println("Error while parsing dblp.xml: " + e);
			System.exit(3);
			return;
		}

        System.exit(0);
    }
}

@@ -7441,19 +7441,34 @@
 ut;%0A
-%09%09try %7B%0A%09%09%09
+        try %7B%0A            
 dblp
@@ -7502,17 +7502,20 @@
 (file);%0A
-%09
+    
         
@@ -7623,17 +7623,19 @@
 8);%0A
-%09
+   
         
 coll
@@ -7622,32 +7622,33 @@
 _8);%0A           
+ 
 collectionOutput
@@ -7735,18 +7735,24 @@
 UTF_8);%0A
-%09%09
+        
 %7D catch 
@@ -7771,35 +7771,44 @@
 dException e) %7B%0A
-%09%09%09
+            
 System.err.print
@@ -7840,35 +7840,44 @@
  found: %22 + e);%0A
-%09%09%09
+            
 System.exit(2);%0A
@@ -7876,29 +7876,44 @@
 xit(2);%0A
-%09%09%09return;%0A%09%09
+            return;%0A        
 %7D%0A%0A     
@@ -7921,19 +7921,28 @@
   try %7B%0A
-%09%09%09
+            
 parse(db
@@ -7988,18 +7988,24 @@
 utput);%0A
-%09%09
+        
 %7D catch 
@@ -8043,19 +8043,28 @@
 on e) %7B%0A
-%09%09%09
+            
 System.e
@@ -8113,19 +8113,28 @@
 %22 + e);%0A
-%09%09%09
+            
 System.e
@@ -8145,21 +8145,36 @@
 3);%0A
-%09%09%09return;%0A%09%09
+            return;%0A        
 %7D%0A%0A 

Fix whitespace issues (tabs instead of spaces)