/* **********************************************************************
 * source:    MyMarshallingHelper
 * package:   com.me.my.xml.toys
 * author:    sisjo
 ************************************************************************/
package com.me.my.xml.toys;


import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import java.util.*;
import java.util.logging.Logger;

/*************
 * class: MyMarshallingHelper
 * <p>
 * This is a class to provide both Marshaller and UnMarshaller Listener help in a JaxB world.
 * <p>
 * This class has bulk methods for setting up both Marshaller and Unmarshaller to use encoding,schemas etc.
 */
public final class MyMarshallingHelper extends MyMarshallingBase {

  // ==========================
  private Object        myOuter;
  private boolean       isVerbose;
  private boolean       isDumping;
  private String        myStopAt;
  private Marshaller.Listener     myMarshallingListener;
  private Unmarshaller.Listener   myUnMarshallingListener;
  private MySchemaHelper mySH;

  /*************
   * default ctor
   * @param outer
   */
  public MyMarshallingHelper(Object outer) {
    this(outer, false, null, false, null);
  }

  /*************
   * nother ctor
   * @param outer
   * @param bVerbose
   * @param logger
   */
  public MyMarshallingHelper(Object outer, boolean bVerbose, Logger logger) {
    this(outer, bVerbose, logger, false, null);
  }

  /*************
   * smart ctor
   * @param outer         Marshaller output object
   * @param bVerbose      t/f
   * @param logger        Logger
   * @param dumpStack     t/f
   * @param stopAtMethod  stop dumping stack at this method
   */
  public MyMarshallingHelper(Object outer, boolean bVerbose, Logger logger, boolean dumpStack, String stopAtMethod) {
    super(logger);
    myOuter = outer;
    myStopAt = stopAtMethod;
    isVerbose = (bVerbose && (logger != null));
    isDumping = (isVerbose && (myStopAt != null));
    myMarshallingListener = null;
    myUnMarshallingListener = null;
    mySH = MySchemaHelper.getInstance();
  }

  /*************
   * get the marshaller output widget
   * @return
   */
  public Object getOuter() {
    return myOuter;
  }

  /*************
   * set the marshaller outer widget
   * @param outer
   */
  public void setOuter(Object outer) {
    myOuter = outer;
  }

  /*************
   * set verbosity
   * @param bEnable
   */
  public void setVerbosity(boolean bEnable) {
    isVerbose = bEnable;
  }

  /*************
   * get schemahelper
   * @return
   */
  public MySchemaHelper getSchemaHelper() {
    return mySH;
  }

  /*************
   * get the actual XmlMarshalling helper
   * @return
   */
  public Marshaller.Listener getMarshallerListener() {
    if (myMarshallingListener == null) {
      myMarshallingListener = new MyMarshallingListener(this);
    }
    return myMarshallingListener;
  }

  /*************
   * get the actual XmlUnMarshalling helper
   * @return
   */
  public Unmarshaller.Listener getUnmarshallerListener() {
    if (myUnMarshallingListener == null) {
      myUnMarshallingListener = new MyUnMarshallingListener(this);
    }
    return myUnMarshallingListener;
  }

  /*************
   * global before
   * @param actor
   */
  public void beforeMarshallingAction(Object actor) {
    if (isVerbose) {
      reportInfo("befor: " + actor.getClass().getName());
      if (isDumping) {
        dumpStack(new Exception().fillInStackTrace().getStackTrace(), myStopAt);
      }
    }
  }

  /*************
   * global after
   * @param actor
   */
  public void afterMarshallingAction(Object actor) {
    if (isVerbose) {
      reportInfo("after: " + actor.getClass().getName());
      if (isDumping) {
        dumpStack(new Exception().fillInStackTrace().getStackTrace(), myStopAt);
      }
    }
  }

  /*************
   * set up a marshaller from a properties file
   *
   * @param mm
   * @param setEncoding
   * @param setFormatted
   * @param props
   * @param propKey
   * @return
   * @throws Exception
   */
  public boolean setupMarshaller(Marshaller mm, boolean setFormatted, boolean setEncoding, boolean setSchema,
                                 boolean setValidator, boolean setListener, Properties props, String propKey,
                                 String schemaKey,  MyMarshallingValidator validator) throws Exception {
    String encodingKey = ((props != null) ? props.getProperty(badStr(propKey, "?"), "?") : "?");
    return setupMarshaller(mm, setFormatted, setEncoding, setSchema, setValidator, setListener, encodingKey, schemaKey, validator);
  }

  /*************
   * set up a marshaller from an explicit encoding key
   *
   * @param mm
   * @param setListener
   * @param encodingKey
   * @param schemaKey
   * @param validator
   * @return
   * @throws Exception
   */
  public boolean setupMarshaller(Marshaller mm, boolean setListener, String encodingKey,
                                 String schemaKey, MyMarshallingValidator validator) throws Exception {
    boolean setFormatted = true;
    boolean setEncoding = okStr(encodingKey, true);
    boolean setSchema = okStr(schemaKey, true);
    boolean setValidator = (validator != null);
    return setupMarshaller(mm, setFormatted, setEncoding, setSchema, setValidator, setListener, encodingKey, schemaKey, validator);
  }

  /*************
   * set up a marshaller from an explicit encoding key
   *
   * @param mm
   * @param setFormatted
   * @param setEncoding
   * @param setSchema
   * @param setValidator
   * @param setListener
   * @param encodingKey
   * @param schemaKey
   * @param validator
   * @return
   * @throws Exception
   */
  public boolean setupMarshaller(Marshaller mm, boolean setFormatted, boolean setEncoding, boolean setSchema,
                                 boolean setValidator, boolean setListener, String encodingKey,
                                 String schemaKey, MyMarshallingValidator validator) throws Exception {
    boolean ok = (mm != null);
    if (ok && setFormatted) {
      mm.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    }
    if (ok && setEncoding) {
      String encoding = badStr(mySH.getEncodingCS(encodingKey), MySchemaHelper.Enc_Utf_8);
      mm.setProperty(Marshaller.JAXB_ENCODING, encoding);
    }
    if (ok && setSchema) {
      Schema sm = mySH.getSchema(schemaKey);
      ok = (sm != null);
      if (ok) {
        mm.setSchema(sm);
      }
    }
    if (ok && setValidator) {
      if (validator == null) {
        validator = new MyMarshallingValidator(super.getLogger(), null);
      }
      validator.clear();
      mm.setEventHandler(validator);
    }
    if (ok && setListener) {
      mm.setListener(this.getMarshallerListener());
    }
    return ok;
  }

  /*************
   * set up an unmarshaller
   *
   * @param uu
   * @param setListener
   * @param schemaKey
   * @param validator
   * @return
   * @throws Exception
   */
  public boolean setupUnmarshaller(Unmarshaller uu, boolean setListener, String schemaKey, MyMarshallingValidator validator) throws Exception {
    boolean setSchema = okStr(schemaKey, true);
    boolean setValidator = (validator != null);
    return setupUnmarshaller(uu, setSchema, setValidator, setListener, schemaKey, validator);
  }

  /*************
   * set up an unmarshaller
   *
   * @param uu
   * @param setSchema
   * @param setValidator
   * @param setListener
   * @param schemaKey
   * @param validator
   * @return
   * @throws Exception
   */
  public boolean setupUnmarshaller(Unmarshaller uu, boolean setSchema, boolean setValidator, boolean setListener,
                                   String schemaKey, MyMarshallingValidator validator) throws Exception {
    boolean ok = (uu != null);
    if (ok && setSchema) {
      Schema sm = mySH.getSchema(schemaKey);
      ok = (sm != null);
      if (ok) {
        uu.setSchema(sm);  
      }
    }
    if (ok && setValidator && (validator != null)) {
      validator.clear();
      uu.setEventHandler(validator);
    }
    if (ok && setListener) {
      uu.setListener(this.getUnmarshallerListener());
    }
    return ok;
  }

  /*************
   * worker
   * @param astk
   * @param stopAt
   */
  private void dumpStack(StackTraceElement[] astk, String stopAt) {
    boolean stopped = false;
    Iterator<StackTraceElement> it = Arrays.asList(astk).iterator();
    while (!stopped && it.hasNext()) {
      StackTraceElement stk = it.next();
      reportInfo("  stk: " + stk.getClassName() + ':' + stk.getMethodName() + '@' + stk.getLineNumber());
      if (myStopAt != null) {
        stopped = stk.getMethodName().equalsIgnoreCase(stopAt);
      }
    }
  }

  /*************************
   * worker bee
   */
  static private class MyMarshallingListener extends Marshaller.Listener {
    private MyMarshallingHelper myHost;
    public MyMarshallingListener(MyMarshallingHelper host) {
      myHost = host;
    }
    public void beforeMarshal(Object source) {
      try {myHost.beforeMarshallingAction(source); } catch(Throwable th) {}
    }
    public void afterMarshal(Object source) {
      try {myHost.afterMarshallingAction(source);  } catch(Throwable th) {}
    }
  } //endinner

  /*************************
   * worker bee
   */
  static private class MyUnMarshallingListener extends Unmarshaller.Listener {
    private MyMarshallingHelper myHost;
    public MyUnMarshallingListener(MyMarshallingHelper host) {
      myHost = host;
    }
    public void beforeUnmarshal(Object target, Object parent) {
      try {myHost.beforeMarshallingAction(target); } catch(Throwable th) {}
    }
    public void afterUnmarshal(Object target, Object parent) {
      try {myHost.afterMarshallingAction(target);  } catch(Throwable th) {}
    }
  } //endinner
} //endclass
