/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.epimorphics.registry.csv;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.epimorphics.util.EpiException;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.shared.PrefixMapping;

/**
 * Support for writing RDF resources encoded into CSV files.
 * In typical use should set baseURI, any prefixes and then any headers before starting writing.
 */
public class CSVRDFWriter extends CSVBaseWriter {
    public static final String ID_COL = "@id";
    
    protected PrefixMapping prefixes;
    protected String baseURI;
    protected Set<String> headers = new HashSet<>();
    protected boolean started = false;
    
    public CSVRDFWriter(OutputStream out, PrefixMapping mapping) {
        super(out);
        this.prefixes = mapping;
    }
    
    /**
     * Provide an optional baseURI to which all resource URIs will be relativised.
     */
    public void setBaseURI(String baseURI) {
        this.baseURI = baseURI;
    }
    
    /**
     * Register an additional header to be included in the CSV.
     * Non-RDF headers should start with "@".
     */
    public void addHeader(String header) {
        headers.add(header);
    }
    
    /**
     * Register a set of additional header to be included in the CSV.
     * Non-RDF headers should start with "@".
     */
    public void addHeader(String[] headers) {
        for (String header: headers) addHeader(header);
    }
    
    /**
     * Register headers needed to serialize the given Resource  
     */
    public void addHeader(Resource r) {
        addHeader( Collections.singletonList(r) );
    }
    
    /**
     * Register headers needed to serialize the given set of Resources  
     */
    public void addHeader(List<Resource> resources) {
        Set<Property> props = new HashSet<>();
        for (Resource r : resources) {
            for (StmtIterator i = r.listProperties(); i.hasNext();) {
                props.add( i.next().getPredicate() );
            }
        }
        for (Property prop : props) {
            addHeader( RDFCSVUtil.encode(prop, prefixes) );
        }
    }
    
    protected void checkStarted() {
        if (!started) {
            headers.add( ID_COL );
            List<String> headerList = new ArrayList<>( headers );
            Collections.sort(headerList);
            setHeaders(headerList);
            started = true;
        }
    }
    
    /**
     * Write a resource to the current row.
     * Any property which does not match a registered header is silently ignored.
     * Does not finish the row, so other cells may be added.
     */
    public void write(Resource r) {
        checkStarted();
        String uri = r.getURI();
        if (uri == null) {
            uri = "";
        } else {
            if (baseURI != null && uri.startsWith(baseURI)) {
                uri = uri.substring(baseURI.length());
            }
        }
        write(ID_COL, "<" + uri + ">");
        for (StmtIterator i = r.listProperties(); i.hasNext();) {
            Statement s = i.next();
            String header = RDFCSVUtil.encode(s.getPredicate(), prefixes);
            String value = RDFCSVUtil.encode(s.getObject(), prefixes);
            try {
                write(header, value);
            } catch (EpiException e) {
                // Ignore
            }
        }
    }
    
}

@@ -890,118 +890,9 @@
 til.
-ArrayList;%0Aimport java.util.Collections;%0Aimport java.util.HashSet;%0Aimport java.util.List;%0Aimport java.util.Set
+*
 ;%0A%0Ai
@@ -965,24 +965,17 @@
 f.model.
-Property
+*
 ;%0Aimport
@@ -995,162 +995,73 @@
 ena.
-rdf.model.Resource;%0Aimport org.apache.jena.rdf.model.Statement;%0Aimport org.apache.jena.rdf.model.StmtIterator;%0Aimport org.apache.jena.shared.PrefixMapping
+shared.PrefixMapping;%0A%0Aimport javax.ws.rs.core.MultivaluedHashMap
 ;%0A%0A/
@@ -3786,99 +3786,517 @@
     
-for (StmtIterator i = r.listProperties(); i.hasNext();) %7B%0A            Statement s = i.next(
+MultivaluedHashMap%3CProperty, RDFNode%3E valuesByProp = new MultivaluedHashMap%3C%3E();%0A        r.listProperties().forEachRemaining(stmt -%3E %7B%0A            valuesByProp.add(stmt.getPredicate(), stmt.getObject());%0A        %7D);%0A        valuesByProp.forEach((prop, nodes) -%3E %7B%0A            nodes.sort(Comparator.comparing((node) -%3E %7B%0A                if (node.isLiteral()) %7B%0A                    return node.asLiteral().getLanguage();%0A                %7D else %7B%0A                    return null;%0A                %7D%0A            %7D)
 );%0A 
@@ -4344,24 +4344,12 @@
 ode(
-s.getPredicate()
+prop
 , pr
@@ -4349,32 +4349,72 @@
 rop, prefixes);%0A
+            nodes.forEach(node -%3E %7B%0A    
             Stri
@@ -4446,21 +4446,12 @@
 ode(
-s.getObject()
+node
 , pr
@@ -4471,16 +4471,20 @@
         
+    
 try %7B%0A  
@@ -4493,24 +4493,28 @@
             
+   
+ 
 write(header
@@ -4523,16 +4523,20 @@
 value);%0A
+    
         
@@ -4562,24 +4562,28 @@
 eption e) %7B%0A
+    
             
@@ -4600,32 +4600,36 @@
 ore%0A            
+    
 %7D%0A        %7D%0A    
@@ -4622,23 +4622,36 @@
         
-%7D%0A
     %7D
+);%0A        %7D);
 %0A    
+%7D
 %0A%7D%0A

Sort languages in CSV