package net.sf.jasperreports.engine.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import net.sf.jasperreports.engine.JRDatasetRun;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.design.JRDesignChart;
import net.sf.jasperreports.engine.design.JRDesignDataset;
import net.sf.jasperreports.engine.design.JRDesignElement;
import net.sf.jasperreports.engine.design.JRDesignElementDataset;
import net.sf.jasperreports.engine.design.JRDesignImage;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.design.JRDesignVariable;
import net.sf.jasperreports.engine.design.JRValidationException;
import net.sf.jasperreports.engine.design.JasperDesign;
import org.apache.commons.digester.Digester;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class JRXmlLoader {
  private JasperDesign jasperDesign = null;
  
  private Collection groupReprintedElements = new ArrayList();
  
  private Collection groupEvaluatedImages = new ArrayList();
  
  private Collection groupEvaluatedTextFields = new ArrayList();
  
  private Collection groupEvaluatedCharts = new ArrayList();
  
  private Set groupBoundDatasets = new HashSet();
  
  private List errors = new ArrayList();
  
  private Digester digester = null;
  
  private boolean ignoreConsistencyProblems;
  
  public JRXmlLoader(Digester digester) {
    this.digester = digester;
  }
  
  public void setJasperDesign(JasperDesign jasperDesign) {
    this.jasperDesign = jasperDesign;
  }
  
  public Collection getGroupReprintedElements() {
    return this.groupReprintedElements;
  }
  
  public Collection getGroupEvaluatedImages() {
    return this.groupEvaluatedImages;
  }
  
  public Collection getGroupEvaluatedTextFields() {
    return this.groupEvaluatedTextFields;
  }
  
  public Collection getGroupEvaluatedCharts() {
    return this.groupEvaluatedCharts;
  }
  
  public Set getGroupBoundDatasets() {
    return this.groupBoundDatasets;
  }
  
  public static JasperDesign load(String sourceFileName) throws JRException {
    return load(new File(sourceFileName));
  }
  
  public static JasperDesign load(File file) throws JRException {
    JasperDesign jasperDesign = null;
    FileInputStream fis = null;
    try {
      fis = new FileInputStream(file);
      jasperDesign = load(fis);
    } catch (IOException e) {
      throw new JRException(e);
    } finally {
      if (fis != null)
        try {
          fis.close();
        } catch (IOException e) {} 
    } 
    return jasperDesign;
  }
  
  public static JasperDesign load(InputStream is) throws JRException {
    JasperDesign jasperDesign = null;
    JRXmlLoader xmlLoader = null;
    try {
      xmlLoader = new JRXmlLoader(JRXmlDigesterFactory.createDigester());
    } catch (ParserConfigurationException e) {
      throw new JRException(e);
    } catch (SAXException e) {
      throw new JRException(e);
    } 
    jasperDesign = xmlLoader.loadXML(is);
    return jasperDesign;
  }
  
  public JasperDesign loadXML(InputStream is) throws JRException {
    return loadXML(new InputSource(is));
  }
  
  public JasperDesign loadXML(InputSource is) throws JRException {
    try {
      this.digester.push(this);
      this.digester.parse(is);
    } catch (SAXException e) {
      throw new JRException(e);
    } catch (IOException e) {
      throw new JRException(e);
    } finally {
      this.digester.clear();
    } 
    if (this.errors.size() > 0) {
      Exception e = this.errors.get(0);
      if (e instanceof JRException)
        throw (JRException)e; 
      throw new JRException(e);
    } 
    assignGroupsToVariables(this.jasperDesign.getMainDesignDataset());
    for (Iterator it = this.jasperDesign.getDatasetsList().iterator(); it.hasNext(); ) {
      JRDesignDataset dataset = it.next();
      assignGroupsToVariables(dataset);
    } 
    assignGroupsToElements();
    assignGroupsToImages();
    assignGroupsToTextFields();
    assignGroupsToCharts();
    assignGroupsToDatasets();
    return this.jasperDesign;
  }
  
  private void assignGroupsToVariables(JRDesignDataset dataset) throws JRException {
    JRVariable[] variables = dataset.getVariables();
    if (variables != null && variables.length > 0) {
      Map groupsMap = dataset.getGroupsMap();
      for (int i = 0; i < variables.length; i++) {
        JRDesignVariable variable = (JRDesignVariable)variables[i];
        if (variable.getResetType() == 4) {
          String groupName = null;
          JRGroup group = variable.getResetGroup();
          if (group != null) {
            groupName = group.getName();
            group = (JRGroup)groupsMap.get(groupName);
          } 
          if (!this.ignoreConsistencyProblems && group == null)
            throw new JRValidationException("Unknown reset group '" + groupName + "' for variable : " + variable.getName(), variable); 
          variable.setResetGroup(group);
        } else {
          variable.setResetGroup(null);
        } 
        if (variable.getIncrementType() == 4) {
          String groupName = null;
          JRGroup group = variable.getIncrementGroup();
          if (group != null) {
            groupName = group.getName();
            group = (JRGroup)groupsMap.get(groupName);
          } 
          if (!this.ignoreConsistencyProblems && group == null)
            throw new JRValidationException("Unknown increment group '" + groupName + "' for variable : " + variable.getName(), variable); 
          variable.setIncrementGroup(group);
        } else {
          variable.setIncrementGroup(null);
        } 
      } 
    } 
  }
  
  private void assignGroupsToElements() throws JRException {
    Map groupsMap = this.jasperDesign.getGroupsMap();
    for (Iterator it = this.groupReprintedElements.iterator(); it.hasNext(); ) {
      JRDesignElement element = it.next();
      String groupName = null;
      JRGroup group = element.getPrintWhenGroupChanges();
      if (group != null) {
        groupName = group.getName();
        group = (JRGroup)groupsMap.get(group.getName());
      } 
      if (!this.ignoreConsistencyProblems && group == null)
        throw new JRValidationException("Unknown reprint group '" + groupName + "' for element.", element); 
      element.setPrintWhenGroupChanges(group);
    } 
  }
  
  private void assignGroupsToImages() throws JRException {
    Map groupsMap = this.jasperDesign.getGroupsMap();
    for (Iterator it = this.groupEvaluatedImages.iterator(); it.hasNext(); ) {
      JRDesignImage image = it.next();
      String groupName = null;
      JRGroup group = image.getEvaluationGroup();
      if (group != null) {
        groupName = group.getName();
        group = (JRGroup)groupsMap.get(group.getName());
      } 
      if (!this.ignoreConsistencyProblems && group == null)
        throw new JRValidationException("Unknown evaluation group '" + groupName + "' for image.", image); 
      image.setEvaluationGroup(group);
    } 
  }
  
  private void assignGroupsToTextFields() throws JRException {
    Map groupsMap = this.jasperDesign.getGroupsMap();
    for (Iterator it = this.groupEvaluatedTextFields.iterator(); it.hasNext(); ) {
      JRDesignTextField textField = it.next();
      String groupName = null;
      JRGroup group = textField.getEvaluationGroup();
      if (group != null) {
        groupName = group.getName();
        group = (JRGroup)groupsMap.get(group.getName());
      } 
      if (!this.ignoreConsistencyProblems && group == null)
        throw new JRValidationException("Unknown evaluation group '" + groupName + "' for text field.", textField); 
      textField.setEvaluationGroup(group);
    } 
  }
  
  private void assignGroupsToCharts() throws JRException {
    Map groupsMap = this.jasperDesign.getGroupsMap();
    for (Iterator it = this.groupEvaluatedCharts.iterator(); it.hasNext(); ) {
      JRDesignChart chart = it.next();
      String groupName = null;
      JRGroup group = chart.getEvaluationGroup();
      if (group != null) {
        groupName = group.getName();
        group = (JRGroup)groupsMap.get(group.getName());
      } 
      if (!this.ignoreConsistencyProblems && group == null)
        throw new JRValidationException("Unknown evaluation group '" + groupName + "' for chart.", chart); 
      chart.setEvaluationGroup(group);
    } 
  }
  
  private void assignGroupsToDatasets() throws JRException {
    for (Iterator it = this.groupBoundDatasets.iterator(); it.hasNext(); ) {
      Map groupsMap;
      JRDesignElementDataset dataset = it.next();
      JRDatasetRun datasetRun = dataset.getDatasetRun();
      if (datasetRun == null) {
        groupsMap = this.jasperDesign.getGroupsMap();
      } else {
        Map datasetMap = this.jasperDesign.getDatasetMap();
        String datasetName = datasetRun.getDatasetName();
        JRDesignDataset subDataset = (JRDesignDataset)datasetMap.get(datasetName);
        if (subDataset == null)
          throw new JRException("Unknown sub dataset '" + datasetName + "' for chart dataset."); 
        groupsMap = subDataset.getGroupsMap();
      } 
      if (dataset.getIncrementType() == 4) {
        String groupName = null;
        JRGroup group = dataset.getIncrementGroup();
        if (group != null) {
          groupName = group.getName();
          group = (JRGroup)groupsMap.get(group.getName());
        } 
        if (!this.ignoreConsistencyProblems && group == null)
          throw new JRValidationException("Unknown increment group '" + groupName + "' for chart dataset.", dataset); 
        dataset.setIncrementGroup(group);
      } else {
        dataset.setIncrementGroup(null);
      } 
      if (dataset.getResetType() == 4) {
        String groupName = null;
        JRGroup group = dataset.getResetGroup();
        if (group != null) {
          groupName = group.getName();
          group = (JRGroup)groupsMap.get(group.getName());
        } 
        if (!this.ignoreConsistencyProblems && group == null)
          throw new JRValidationException("Unknown reset group '" + groupName + "' for chart dataset.", dataset); 
        dataset.setResetGroup(group);
        continue;
      } 
      dataset.setResetGroup(null);
    } 
  }
  
  public void addError(Exception e) {
    if (!this.ignoreConsistencyProblems)
      this.errors.add(e); 
  }
  
  public boolean isIgnoreConsistencyProblems() {
    return this.ignoreConsistencyProblems;
  }
  
  public void setIgnoreConsistencyProblems(boolean ignoreConsistencyProblems) {
    this.ignoreConsistencyProblems = ignoreConsistencyProblems;
  }
}
