ChronoRepository.java

package org.docascode.api.core.chrono;

import org.apache.commons.io.FilenameUtils;
import org.docascode.api.core.chrono.generated.Maven;
import org.docascode.api.core.errors.DocAsCodeException;
import org.docascode.api.core.Substitute;
import org.docascode.api.core.chrono.generated.BaseArtifact;
import org.docascode.api.core.chrono.generated.Delivery;
import org.docascode.api.core.errors.ArtifactNotFoundException;
import org.docascode.api.core.errors.ProcessingException;
import org.docascode.api.core.mvn.ArtifactContainer;
import org.docascode.api.core.office.PropertiesProcessor;
import org.docascode.api.event.Event;
import org.docascode.api.listener.APIEventListener;
import org.docascode.api.listener.EventListener;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.File;
import java.io.InputStream;
import java.util.*;

import static org.docascode.api.event.Event.Level.WARN;

public class ChronoRepository extends APIEventListener {
    private static final String ARTIFACT_NOT_FOUND = "Artifact '%s' not found.";

    private File chronoXML;

    private File baseDir;

    private Delivery delivery = null;

    public ChronoRepository(File chronoXML, File baseDir){
        this.baseDir = baseDir;
        this.chronoXML = chronoXML;
    }

    private Delivery load() throws ProcessingException {
        try {
            JAXBContext context = JAXBContext.newInstance(Delivery.class);
            Unmarshaller un = context.createUnmarshaller();
            //Setup schema validator
            SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("org/docascode/api/core/chrono.xsd");
            StreamSource xsd = new StreamSource(resourceAsStream);
                    Schema schema = sf.newSchema(xsd);
            un.setSchema(schema);
            return (Delivery) un.unmarshal(chronoXML);
        } catch (JAXBException | SAXException e) {
            throw new ProcessingException("Unable to unmarshal file "+chronoXML.getAbsolutePath(),e);
        }
    }

    public Delivery getDelivery() throws ProcessingException {
        if (delivery == null){
            delivery = load();
        }
        return delivery;
    }

    public void save(Delivery delivery) throws ProcessingException {
        this.delivery = delivery;
        try {
            JAXBContext context = JAXBContext.newInstance(Delivery.class);
            Marshaller m = context.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            m.marshal(delivery, chronoXML);
        } catch (javax.xml.bind.JAXBException e) {
            throw new ProcessingException("Unable to marshal delivery to file "+chronoXML.getAbsolutePath(),e);
        }
    }

    public ArtifactContainer toArtifacts(List<String> ids) throws DocAsCodeException {
        ArtifactContainer result = new ArtifactContainer();
        for (String id : ids){
            org.docascode.api.core.chrono.generated.Artifact a = getDelivery().getArtifacts().get(id);
            if (a==null){
                throw new ArtifactNotFoundException(
                        String.format(ARTIFACT_NOT_FOUND,
                                id));
            } else {
                Maven n = a.getMaven();
                if (n != null) {
                    if (a.getFile() == null) {
                        Event e = new Event(this);
                        e.setMessage(String.format(
                                "Main Artifact '%s' is not deployable since it has no 'file' field.", id)
                        );
                        fireEvent(e);
                    } else {
                        Map<String, String> dict = new PropertiesProcessor().list(new File(baseDir, a.getFile()));
                        org.eclipse.aether.artifact.Artifact artifact =
                                new DefaultArtifact(Substitute.substitute(n.getGroupId(), dict),
                                        Substitute.substitute(n.getArtifactId(), dict),
                                        Substitute.substitute("", dict),
                                        getExtension(a),
                                        Substitute.substitute(n.getVersion(), dict));
                        artifact = artifact.setFile(new File(baseDir, a.getFile()));
                        result.add(artifact);
                        if (a.getAttach() != null) {
                            for (Map.Entry<String, BaseArtifact> baseArtifactEntry : a.getAttach().entrySet()) {
                                BaseArtifact baseArtifact = baseArtifactEntry.getValue();
                                if (baseArtifact.getFile() == null) {
                                    Event e = new Event(this);
                                    e.setMessage(String.format(
                                            "Attached Artifact '%s:%s' is not deployable since it has no 'file' field.",
                                            id,
                                            baseArtifactEntry.getKey()));
                                    fireEvent(e);
                                } else {
                                    artifact =
                                            new DefaultArtifact(Substitute.substitute(n.getGroupId(), dict),
                                                    Substitute.substitute(n.getArtifactId(), dict),
                                                    Substitute.substitute(baseArtifactEntry.getKey(), dict),
                                                    getExtension(baseArtifact),
                                                    Substitute.substitute(n.getVersion(), dict));
                                    artifact = artifact.setFile(new File(baseDir, baseArtifact.getFile()));
                                    result.add(artifact);
                                }
                            }
                        }
                    }
                }
            }
        }
        return result;
    }

    private String getExtension(BaseArtifact artifact){
        if (artifact.getFile() != null){
            return FilenameUtils.getExtension(artifact.getFile());
        } else {
            return null;
        }
    }

    public Map<String,String> toMap(String name) throws ProcessingException {
        Map<String,String> result = new HashMap<>();
        for (org.docascode.api.core.chrono.generated.Artifact artifact : getDelivery().getArtifacts().values()){
            if (artifact.getFile() == null ||  artifact.getOutputs()==null || !artifact.getOutputs().containsKey(name)) {
                //Resolve artifact
            } else {
                result.put(artifact.getFile(),artifact.getOutputs().get(name));
            }
            if (artifact.getAttach() != null) {
                for (BaseArtifact baseArtifact : artifact.getAttach().values()) {
                    if (baseArtifact.getFile() == null ||  baseArtifact.getOutputs()==null || !baseArtifact.getOutputs().containsKey(name)) {
                        //Resolve artifact
                    } else {
                        result.put(baseArtifact.getFile(),
                                baseArtifact.getOutputs().get(name));
                    }
                }
            }
        }
        return result;
    }

    @Override
    public ChronoRepository addListener(EventListener listener){
        this.listeners.add(listener);
        return this;
    }

    private Set<EventListener> listeners = new HashSet<>();

    @Override
    public void fireEvent(Event e){
        for (EventListener l : this.listeners){
            l.fireEvent(e);
        }
    }

    public List<String> toFileNames(List<String> ids) throws ArtifactNotFoundException, ProcessingException {
        List<String> result = new ArrayList<>();
        for (String id : ids){
            org.docascode.api.core.chrono.generated.Artifact a = getDelivery().getArtifacts().get(id);
            if (a==null){
                throw new ArtifactNotFoundException(
                        String.format(ARTIFACT_NOT_FOUND,
                                id));
            } else {
                if (a.getFile() != null) {
                    result.add(a.getFile());
                    }
                if (a.getAttach() != null) {
                    for (Map.Entry<String, BaseArtifact> baseArtifactEntry : a.getAttach().entrySet()) {
                        BaseArtifact baseArtifact = baseArtifactEntry.getValue();
                        if (baseArtifact.getFile() != null) {
                            result.add(baseArtifact.getFile());
                        }
                    }
                }
            }
        }
        return result;
    }

    private Map<String, Artifact> artifactFileMap;

    public Map<String, Artifact> toArtifactFileMap() throws DocAsCodeException {
        if (artifactFileMap==null) {
            artifactFileMap = new HashMap<>();
            for (Map.Entry<String, org.docascode.api.core.chrono.generated.Artifact> entry :
                    getDelivery().getArtifacts().entrySet()) {
                org.docascode.api.core.chrono.generated.Artifact a = entry.getValue();
                Maven n = a.getMaven();
                if (n != null) {
                    if (a.getFile() == null) {
                        Event e = new Event(this);
                        e.setMessage(String.format(
                                "Main Artifact '%s' is not deployable since it has no 'file' field.", entry.getKey())
                        );
                        fireEvent(e);
                    } else {
                        if (!(new File(this.baseDir, a.getFile())).exists()) {
                            log(String.format("The file '%s' is missing for artifact '%s'. This artifact and all nested artifacts are not deployable.", a.getFile(), entry.getKey()), WARN);
                        } else {
                            Map<String, String> dict = new PropertiesProcessor().list(new File(this.baseDir,a.getFile()));
                            org.eclipse.aether.artifact.Artifact artifact =
                                    new DefaultArtifact(Substitute.substitute(n.getGroupId(), dict),
                                            Substitute.substitute(n.getArtifactId(), dict),
                                            Substitute.substitute("", dict),
                                            getExtension(a),
                                            Substitute.substitute(n.getVersion(), dict));
                            artifact = artifact.setFile(new File(this.baseDir, a.getFile()));
                            artifactFileMap.put(a.getFile(), artifact);
                            if (a.getAttach() != null) {
                                for (Map.Entry<String, BaseArtifact> baseArtifactEntry : a.getAttach().entrySet()) {
                                    BaseArtifact baseArtifact = baseArtifactEntry.getValue();
                                    if (baseArtifact.getFile() == null) {
                                        Event e = new Event(this);
                                        e.setMessage(String.format(
                                                "Attached Artifact '%s:%s' is not deployable since it has no 'file' field.",
                                                entry.getKey(),
                                                baseArtifactEntry.getKey()));
                                        fireEvent(e);
                                    } else {
                                        artifact =
                                                new DefaultArtifact(Substitute.substitute(n.getGroupId(), dict),
                                                        Substitute.substitute(n.getArtifactId(), dict),
                                                        Substitute.substitute(baseArtifactEntry.getKey(), dict),
                                                        getExtension(baseArtifact),
                                                        Substitute.substitute(n.getVersion(), dict));
                                        artifact = artifact.setFile(new File(this.baseDir, baseArtifact.getFile()));
                                        artifactFileMap.put(baseArtifact.getFile(), artifact);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return artifactFileMap;
    }
}