All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bedework.calfacade.base.DumpEntity Maven / Gradle / Ivy

The newest version!
/* ********************************************************************
    Licensed to Jasig under one or more contributor license
    agreements. See the NOTICE file distributed with this work
    for additional information regarding copyright ownership.
    Jasig 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 org.bedework.calfacade.base;

import org.bedework.base.exc.BedeworkException;
import org.bedework.base.exc.persist.BedeworkDatabaseException;
import org.bedework.calfacade.annotations.Dump;
import org.bedework.calfacade.annotations.Dump.DumpFormat;
import org.bedework.calfacade.annotations.NoDump;
import org.bedework.calfacade.annotations.NoWrap;
import org.bedework.calfacade.exc.CalFacadeErrorCode;
import org.bedework.util.logging.BwLogger;
import org.bedework.util.logging.Logged;
import org.bedework.util.xml.XmlEmit;

import net.fortuna.ical4j.vcard.VCard;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.TreeSet;

import javax.xml.namespace.QName;

/** An entity which can be dumped.
 *
 * @author Mike Douglass
 * @version 1.0
 *
 * @param 
 */
public class DumpEntity implements Logged {
  /** We're dumping the entire object */
  public enum DumpType {
    /** We're dumping the entire object */
    def,

    /** We're dumping a compound type */
    compound,

    /** We're dumping enough to refer to an entity */
    reference
  }

  /** Override this if we want to optionally suppress the dump based on some
   * attributes. This allows us to skip empty objects which occasionally turn
   * up.
   *
   * @return boolean true to continue with dump.
   */
  @NoWrap
  public boolean hasDumpValue() {
    return true;
  }

  /** Dump the entire entity into the given file.
   *
   * @param f - the file
   */
  @NoWrap
  public void dump(final File f) {
    final Dump dCl = getClass().getAnnotation(Dump.class);

    if (dCl.format() == DumpFormat.xml) {
      Writer wtr = null;

      try {
        final XmlEmit xml = new XmlEmit();
        wtr = new FileWriter(f);
        xml.startEmit(wtr);

        dump(xml, DumpType.def, false);
        xml.flush();
        return;
      } catch (final IOException e) {
        throw new BedeworkDatabaseException(e);
      } finally {
        if (wtr != null) {
          try {
            wtr.close();
          } catch (final Throwable t) {
            throw new BedeworkDatabaseException(t);
          }
        }
      }
    }

    if (dCl.format() == DumpFormat.vCard) {
      Writer wtr = null;

      try {
        final VCard vc = new VCard();

        dump(vc, DumpType.def);

        final String vcStr = vc.toString();
        wtr = new FileWriter(f);
        wtr.append(vcStr);
        return;
      } catch (final Throwable t) {
        throw new BedeworkDatabaseException(t);
      } finally {
        if (wtr != null) {
          try {
            wtr.close();
          } catch (final Throwable t) {
            throw new BedeworkDatabaseException(t);
          }
        }
      }
    }

    throw new BedeworkDatabaseException("Unsupported dump format " + dCl.format());
  }


  /** Dump the entire entity.
   *
   * @param xml emitter
   */
  @NoWrap
  public void dump(final XmlEmit xml) {
    dump(xml, DumpType.def, false);
  }

  /* ====================================================================
   *                   Private XML methods
   * ==================================================================== */

  /** Dump this entity as xml.
   *
   * @param xml emitter
   * @param dtype
   * @param fromCollection  true if the value is a member of a collection
   */
  @NoWrap
  private void dump(final XmlEmit xml,
                    final DumpType dtype,
                    final boolean fromCollection) {
    if (!hasDumpValue()) {
      return;
    }

    final NoDump ndCl = getClass().getAnnotation(NoDump.class);
    final Dump dCl = getClass().getAnnotation(Dump.class);

    final boolean dumpKeyFields = dtype == DumpType.reference;

    ArrayList noDumpMethods = null;
    ArrayList firstMethods = null;

    if (ndCl != null) {
      if (ndCl.value().length == 0) {
        return;
      }

      noDumpMethods = new ArrayList<>();
      Collections.addAll(noDumpMethods, ndCl.value());
    }

    if (!dumpKeyFields && (dCl != null) && (dCl.firstFields().length != 0)) {
      firstMethods = new ArrayList<>();
      for (final String f: dCl.firstFields()) {
        firstMethods.add(methodName(f));
      }
    }

    QName qn = null;

    if (fromCollection || (dtype != DumpType.compound)) {
      qn = startElement(xml, getClass(), dCl);
    }

    final Collection ms = findGetters(dCl, dtype);

    if (firstMethods != null) {
      doFirstMethods:
      for (final String methodName: firstMethods) {
        for (final ComparableMethod cm: ms) {
          final Method m = cm.m;

          if (methodName.equals(m.getName())) {
            final Dump d = m.getAnnotation(Dump.class);

            try {
              dumpValue(xml, m, d,
                        m.invoke(this, (Object[])null), fromCollection);
            } catch (final  IllegalAccessException
                            | InvocationTargetException e) {
              throw new BedeworkDatabaseException(e);
            }

            continue doFirstMethods;
          }
        }

        error("Listed first field has no corresponding getter: " +
                      methodName);
      }
    }

    for (final ComparableMethod cm: ms) {
      final Method m = cm.m;

      if ((noDumpMethods != null) &&
              noDumpMethods.contains(fieldName(m.getName()))) {
        continue;
      }

      if ((firstMethods != null) &&
              firstMethods.contains(m.getName())) {
        continue;
      }

      final Dump d = m.getAnnotation(Dump.class);

      try {
        dumpValue(xml, m, d,
                  m.invoke(this, (Object[])null), fromCollection);
      } catch (final  IllegalAccessException
                      | InvocationTargetException e) {
        throw new BedeworkDatabaseException(e);
      }
    }

    if (qn != null) {
      closeElement(xml, qn);
    }
  }

  /* ====================================================================
   *                   Private methods
   * ==================================================================== */

  private boolean dumpValue(final XmlEmit xml,
                            final Method m,
                            final Dump d,
                            final Object methVal,
                            final boolean fromCollection) {
    /* We always open the methodName or elementName tag if this is the method
     * value.
     *
     * If this is an element from a collection we generally don't want a tag.
     *
     * We do open a tag if the annotation specifies a collectionElementName
     */
    if (methVal instanceof final DumpEntity de) {
      if (!de.hasDumpValue()) {
        return false;
      }

      final boolean compound = (d!= null) && d.compound();

      final QName mqn = startElement(xml, m, d, fromCollection);

      final DumpType dt;
      if (compound) {
        dt = DumpType.compound;
      } else {
        dt = DumpType.reference;
      }

      de.dump(xml, dt, false);

      if (mqn != null) {
        closeElement(xml, mqn);
      }

      return true;
    }

    if (methVal instanceof final Collection c) {
      if (c.isEmpty()) {
        return false;
      }

      QName mqn = null;

      for (final Object o: c) {
        if ((o instanceof final DumpEntity de) &&
            (!de.hasDumpValue())) {
          continue;
        }

        if (mqn == null) {
          mqn = startElement(xml, m, d, fromCollection);
        }

        dumpValue(xml, m, d, o, true);
      }

      if (mqn != null) {
        closeElement(xml, mqn);
      }

      return true;
    }

    property(xml, m, d, methVal, fromCollection);

    return true;
  }

  private QName startElement(final XmlEmit xml,
                             final Class c,
                             final Dump d) {
    final QName qn;

    if (d == null) {
      qn = new QName(c.getName());
    } else {
      qn = new QName(d.elementName());
    }

    xml.openTag(qn);
    return qn;
  }

  private QName startElement(final XmlEmit xml,
                             final Method m,
                             final Dump d,
                             final boolean fromCollection) {
    final QName qn = getTag(m, d, fromCollection);

    if (qn != null) {
      xml.openTag(qn);
    }

    return qn;
  }

  private QName getTag(final Method m, final Dump d,
                       final boolean fromCollection) {
    String tagName = null;

    if (d != null) {
      if (!fromCollection) {
        if (!d.elementName().isEmpty()) {
          tagName = d.elementName();
        }
      } else if (!d.collectionElementName().isEmpty()) {
        tagName = d.collectionElementName();
      }
    }

    if ((tagName == null) && !fromCollection) {
      tagName = fieldName(m.getName());
    }

    if (tagName == null) {
      return null;
    }

    return new QName(tagName);
  }

  private void property(final XmlEmit xml, final Method m,
                        final Dump d, final Object p,
                        final boolean fromCollection) {
    if (p == null) {
      return;
    }

    QName qn = getTag(m, d, fromCollection);

    if (qn == null) {
      /* Collection and no collection element name specified */
      qn = new QName(p.getClass().getName());
    }

    final String sval;

    if (p instanceof char[]) {
      sval = new String((char[])p);
    } else {
      sval = String.valueOf(p);
    }

    if ((sval.indexOf('&') < 0) && (sval.indexOf('<') < 0)) {
      xml.property(qn, sval);
    } else {
      xml.cdataProperty(qn, sval);
    }
  }

  private void closeElement(final XmlEmit xml,
                            final QName qn) {
    xml.closeTag(qn);
  }

  /* ====================================================================
   *                   Private Vcard methods
   * ==================================================================== */

  /** Dump this entity as a vcard.
   *
   * @param vc
   * @param dtype
   */
  @NoWrap
  private void dump(final VCard vc,
                    final DumpType dtype) {
    if (!hasDumpValue()) {
      return;
    }

    final NoDump ndCl = getClass().getAnnotation(NoDump.class);
    final Dump dCl = getClass().getAnnotation(Dump.class);

    /* If dumpKeyFields is true we are dumping a field which is referred to by
     * a key field - for example, a principal is addressed by the principal
     * href.
     */
    final boolean dumpKeyFields = dtype == DumpType.reference;

    final ArrayList noDumpMethods;
    final ArrayList firstMethods;

    if (ndCl != null) {
      if (ndCl.value().length == 0) {
        return;
      }

      noDumpMethods = new ArrayList<>();
      Collections.addAll(noDumpMethods, ndCl.value());
    }

    if (!dumpKeyFields && (dCl != null) && (dCl.firstFields().length != 0)) {
      firstMethods = new ArrayList<>();
      for (final String f: dCl.firstFields()) {
        firstMethods.add(methodName(f));
      }
    }

      /*
      QName qn = null;

      if (dtype != DumpType.compound) {
        qn = startElement(xml, getClass(), dCl);
      }

      Collection ms = findGetters(dCl, dtype);

      if (firstMethods != null) {
        doFirstMethods:
        for (String methodName: firstMethods) {
          for (ComparableMethod cm: ms) {
            Method m = cm.m;

            if (methodName.equals(m.getName())) {
              Dump d = m.getAnnotation(Dump.class);

              dumpValue(xml, m, d, m.invoke(this, (Object[])null), fromCollection);

              continue doFirstMethods;
            }
          }

          error("Listed first field has no corresponding getter: " + methodName);
        }
      }

      for (ComparableMethod cm: ms) {
        Method m = cm.m;

        if ((noDumpMethods != null) &&
            noDumpMethods.contains(fieldName(m.getName()))) {
          continue;
        }

        if ((firstMethods != null) &&
            firstMethods.contains(m.getName())) {
          continue;
        }

        Dump d = m.getAnnotation(Dump.class);

        dumpValue(xml, m, d, m.invoke(this, (Object[])null), fromCollection);
      }

      if (qn != null) {
        closeElement(xml, qn);
      }
      */
  }

  /* ====================================================================
   *                   Private methods
   * ==================================================================== */

  private static class ComparableMethod implements Comparable {
    Method m;

    ComparableMethod(final Method m) {
      this.m = m;
    }

    @Override
    public int compareTo(final ComparableMethod that) {
      return this.m.getName().compareTo(that.m.getName());
    }
  }

  private Collection findGetters(final Dump d,
                                                   final DumpType dt) {
    final Method[] meths = getClass().getMethods();
    final Collection getters = new TreeSet<>();
    Collection keyMethods = null;

    if (dt == DumpType.reference) {
      if ((d == null) || (d.keyFields().length == 0)) {
        error("No key fields defined for class " + getClass().getCanonicalName());
        throw new BedeworkDatabaseException(CalFacadeErrorCode.noKeyFields);
      }
      keyMethods = new ArrayList<>();
      for (final String f: d.keyFields()) {
        keyMethods.add(methodName(f));
      }
    }

    for (final Method m : meths) {
      final String mname = m.getName();

      if (mname.length() < 4) {
        continue;
      }

      /* Name must start with get */
      if (!mname.startsWith("get")) {
        continue;
      }

      /* Don't want getClass */
      if (mname.equals("getClass")) {
        continue;
      }

      /* No parameters */
      final Class[] parClasses = m.getParameterTypes();
      if (parClasses.length != 0) {
        continue;
      }

      /* Not annotated with NoDump */
      if (m.getAnnotation(NoDump.class) != null) {
        continue;
      }

      /* If we have a list of key methods it must be in that list */
      if ((keyMethods != null) && !keyMethods.contains(mname)) {
        continue;
      }

      getters.add(new ComparableMethod(m));
    }

    return getters;
  }

  private String methodName(final String val) {
    String m = "get" + val.substring(0, 1).toUpperCase();
    if (val.length() > 1) {
      m += val.substring(1);
    }

    return m;
  }

  private String fieldName(final String val) {
    if (val.length() < 4) {
      return null;
    }

    return val.substring(3, 4).toLowerCase() + val.substring(4);
  }

  /* ====================================================================
   *                   Logged methods
   * ==================================================================== */

  private final BwLogger logger = new BwLogger();

  @Override
  public BwLogger getLogger() {
    if ((logger.getLoggedClass() == null) && (logger.getLoggedName() == null)) {
      logger.setLoggedClass(getClass());
    }

    return logger;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy