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

net.projectmonkey.internal.Errors Maven / Gradle / Ivy

/*
 * Copyright 2011 the original author or authors.
 *
 * Licensed 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 net.projectmonkey.internal;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;

import net.projectmonkey.ConfigurationException;
import net.projectmonkey.MappingException;
import net.projectmonkey.TypeMap;
import net.projectmonkey.ValidationException;
import net.projectmonkey.internal.util.Types;
import net.projectmonkey.spi.ErrorMessage;
import net.projectmonkey.spi.PropertyInfo;
import net.projectmonkey.spi.PropertyMapping;


public final class Errors {
  private List errors;

  @SuppressWarnings("rawtypes")
  private static final Converter[] converters = new Converter[] {
      new Converter(Class.class) {
        public String toString(Class type) {
          return type.getName();
        }
      }, new Converter(Member.class) {
        public String toString(Member member) {
          return Types.toString(member);
        }
      }, new Converter(PropertyInfo.class) {
        public String toString(PropertyInfo propertyInfo) {
          return Types.toString(propertyInfo.getMember());
        }
      }, new Converter(Collection.class) {
        public String toString(Collection collection) {
          StringBuilder builder = new StringBuilder();
          boolean first = true;
          for (Object o : collection) {
            if (first)
              first = false;
            else
              builder.append("\n");
            builder.append("\t").append(Errors.convert(o));
          }
          return builder.toString();
        }
      } };

  private static abstract class Converter {
    final Class type;

    Converter(Class type) {
      this.type = type;
    }

    boolean appliesTo(Object subject) {
      return type.isAssignableFrom(subject.getClass());
    }

    String convert(Object subject) {
      return toString(type.cast(subject));
    }

    abstract String toString(T subject);
  }

  public Errors() {
  }

  /** Returns the formatted message for an exception with the specified messages. */
  public static String format(String heading, Collection errorMessages) {
    Formatter fmt = new Formatter().format(heading).format(":%n%n");
    int index = 1;
    boolean displayCauses = getOnlyCause(errorMessages) == null;

    for (ErrorMessage errorMessage : errorMessages) {
      fmt.format("%s) %s%n", index++, errorMessage.getMessage());

      Throwable cause = errorMessage.getCause();
      if (displayCauses && cause != null) {
        StringWriter writer = new StringWriter();
        cause.printStackTrace(new PrintWriter(writer));
        fmt.format("Caused by: %s", writer.getBuffer());
      }

      fmt.format("%n");
    }

    if (errorMessages.size() == 1)
      fmt.format("1 error");
    else
      fmt.format("%s errors", errorMessages.size());

    return fmt.toString();
  }

  public static String format(String messageFormat, Object... arguments) {
    for (int i = 0; i < arguments.length; i++)
      arguments[i] = Errors.convert(arguments[i]);
    return String.format(messageFormat, arguments);
  }

  /**
   * Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
   * zero or multiple messages with causes, null is returned.
   */
  public static Throwable getOnlyCause(Collection messages) {
    Throwable onlyCause = null;
    for (ErrorMessage message : messages) {
      Throwable messageCause = message.getCause();
      if (messageCause == null) {
        continue;
      }

      if (onlyCause != null) {
        return null;
      }

      onlyCause = messageCause;
    }

    return onlyCause;
  }

  private static Object convert(Object source) {
    for (Converter converter : converters)
      if (converter.appliesTo(source))
        return converter.convert(source);
    return source;
  }

  public Errors addError(String message, Throwable throwable) {
    return addMessage(throwable, "An error occured while %s", message);
  }

  public Errors addMessage(ErrorMessage message) {
    if (errors == null)
      errors = new ArrayList();
    errors.add(message);
    return this;
  }

  public Errors addMessage(String message, Object... arguments) {
    return addMessage(null, message, arguments);
  }

  public Errors errorGettingValue(Member member, Throwable t) {
    return addMessage(t, "Failed to get value from %s", member);
  }

  public Errors errorMapping(Object source, Class destinationType) {
    return addMessage("Error mapping %s to %s", source, Types.toString(destinationType));
  }

  public Errors errorMapping(Object source, Class destinationType, Throwable t) {
    return addMessage(t, "Error mapping %s to %s", source, Types.toString(destinationType));
  }

  public Errors errorSettingValue(Member member, Object value, Throwable t) {
    return addMessage(t, "Failed to set value '%s' on %s", value, member);
  }

  public Errors errorTooLarge(Object source, Class destinationType) {
    return addMessage("Value '%s' is too large for %s", source, Types.toString(destinationType));
  }

  public Errors errorTooSmall(Object source, Class destinationType) {
    return addMessage("Value '%s' is too small for %s", source, Types.toString(destinationType));
  }

  public Errors errorUnmappedProperties(TypeMap typeMap, List unmappedProperties) {
    return addMessage("Unmapped destination properties found in %s:\n\n%s", typeMap,
        unmappedProperties);
  }

  public Errors errorCircularReference(Class type) {
    return addMessage("Circular reference detected in %s.", type);
  }

  public Errors errorUnsupportedMapping(Class sourceType, Class destinationType) {
    return addMessage("Missing type map configuration or unsupported mapping for %s to %s.",
        sourceType, destinationType);
  }

  public List getMessages() {
    if (errors == null)
      return Collections.emptyList();
    return errors;
  }

  public boolean hasErrors() {
    return errors != null;
  }

  public Errors merge(Collection errorMessages) {
    for (ErrorMessage message : errorMessages)
      addMessage(message);
    return this;
  }

  public Errors merge(Errors errors) {
    for (ErrorMessage message : errors.getMessages())
      addMessage(message);
    return this;
  }

  public void throwConfigurationExceptionIfErrorsExist() {
    if (hasErrors())
      throw new ConfigurationException(getMessages());
  }

  public void throwValidationExceptionIfErrorsExist() {
    if (hasErrors())
      throw new ValidationException(getMessages());
  }

  public ConfigurationException toConfigurationException() {
    return new ConfigurationException(getMessages());
  }

  public ErrorsException toException() {
    return new ErrorsException(this);
  }

  public MappingException toMappingException() {
    return new MappingException(getMessages());
  }

  Errors ambiguousDestination(Mutator destinationMutator, List mappings) {
    List sourcePropertyInfo = new ArrayList();
    for (PropertyMapping mapping : mappings) {
      StringBuilder builder = new StringBuilder();
      for (int i = 0; i < mapping.getSourceProperties().size(); i++) {
        PropertyInfo info = mapping.getSourceProperties().get(i);
        if (i > 0)
          builder.append("/");
        builder.append(Types.toString(info.getMember()));
      }
      sourcePropertyInfo.add(builder.toString());
    }

    return addMessage(
        "The destination property %s matches multiple source property hierarchies:\n\n%s",
        destinationMutator, sourcePropertyInfo);
  }

  Errors duplicateMapping(PropertyInfo destinationProperty) {
    return addMessage("A mapping already exists for %s.", destinationProperty);
  }

  Errors errorAccessingConfigure(Throwable t) {
    return addMessage(t, "Failed to access PropertyMap.configure().");
  }

  Errors errorAccessingProperty(PropertyInfo propertyInfo) {
    return addMessage("Failed to access %s.", propertyInfo);
  }

  Errors errorConverting(net.projectmonkey.Converter converter, Class sourceType,
      Class destinationType, Throwable throwable) {
    return addMessage(throwable, "Converter %s failed to convert %s to %s.", converter, sourceType,
        destinationType);
  }

  Errors errorEnhancingClass(Class type, Throwable t) {
    return addMessage(t, "Failed to generate proxy class for %s", type);
  }

  Errors errorInstantiatingProxy(Class type, Throwable t) {
    return addMessage(
        t,
        "Failed to instantiate proxied instance of %s. Ensure that %s has a non-private no-argument constructor.",
        type, type);
  }

  public Errors errorInstantiatingDestination(Class type, Throwable t) {
    return addMessage(
        t,
        "Failed to instantiate instance of destination %s. Ensure that %s has a non-private no-argument constructor.",
        type, type);
  }

  Errors invalidDestinationMethod(Method method) {
    return addMessage(
        "Invalid destination method %s. Ensure that method has one parameter and returns void.",
        method);
  }

  Errors invalidSourceMethod(Method method) {
    return addMessage(
        "Invalid source method %s. Ensure that method has zero parameters and does not return void.",
        method);
  }

  Errors invocationAgainstFinalClassOrMethod() {
    return addMessage("Cannot map to final type.");
  }

  Errors missingDestination() {
    return addMessage("A mapping is missing a required destination method.");
  }

  Errors missingMutatorForAccessor(Method method) {
    return addMessage("No corresponding mutator was found for %s.", method);
  }

  Errors missingSource() {
    return addMessage("A mapping is missing a required source method.");
  }

  Errors sourceOutsideOfMap() {
    return addMessage("'source' cannot be used outside of a map statement.");
  }

  void throwMappingExceptionIfErrorsExist() {
    if (hasErrors())
      throw new MappingException(getMessages());
  }

  private Errors addMessage(Throwable cause, String message, Object... arguments) {
    addMessage(new ErrorMessage(format(message, arguments), cause));
    return this;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy