content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF 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.apache.chemistry.opencmis.commons.impl.xml;
  
  import java.lang.reflect.Method;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.GregorianCalendar;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Random;
  import java.util.Set;
  import java.util.TimeZone;
  import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
  import org.apache.chemistry.opencmis.commons.data.PropertyData;
  import org.apache.chemistry.opencmis.commons.data.PropertyId;
  import org.apache.chemistry.opencmis.commons.enums.PropertyType;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyData;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.CmisExtensionElementImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  import org.junit.Before;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Base test class for converter tests.
   */
  public abstract class AbstractConverterTest {
  
      private final static Logger LOG = LoggerFactory.getLogger(AbstractConverterTest.class);
  
      private final static long SEED = 1234567890;
  
      protected Random rnd;
  
      @Before
      public void init() throws Exception {
          rnd = new Random(SEED);
      }
  
      protected PropertyData<?> createPropertyData(PropertyType propertyType, int numValues) {
          AbstractPropertyData<?> result = null;
  
          switch (propertyType) {
          case BOOLEAN:
              result = new PropertyBooleanImpl();
              if (numValues > 0) {
                  List<Boolean> values = new ArrayList<Boolean>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomBoolean());
                  }
                  ((PropertyBooleanImpl) result).setValues(values);
              }
              break;
          case DATETIME:
              result = new PropertyDateTimeImpl();
              if (numValues > 0) {
                  List<GregorianCalendar> values = new ArrayList<GregorianCalendar>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomDateTime());
                  }
                  ((PropertyDateTimeImpl) result).setValues(values);
              }
              break;
          case DECIMAL:
              result = new PropertyDecimalImpl();
              if (numValues > 0) {
                  List<BigDecimal> values = new ArrayList<BigDecimal>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomDecimal());
                  }
                  ((PropertyDecimalImpl) result).setValues(values);
              }
              break;
          case HTML:
              result = new PropertyHtmlImpl();
              if (numValues > 0) {
                  List<String> values = new ArrayList<String>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomString());
                  }
                  ((PropertyHtmlImpl) result).setValues(values);
              }
              break;
          case ID:
              result = new PropertyIdImpl();
              if (numValues > 0) {
                  List<String> values = new ArrayList<String>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomString());
                  }
                  ((PropertyIdImpl) result).setValues(values);
              }
              break;
          case INTEGER:
              result = new PropertyIntegerImpl();
              if (numValues > 0) {
                  List<BigInteger> values = new ArrayList<BigInteger>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomInteger());
                  }
                  ((PropertyIntegerImpl) result).setValues(values);
              }
              break;
          case STRING:
              result = new PropertyStringImpl();
              if (numValues > 0) {
                  List<String> values = new ArrayList<String>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomString());
                  }
                  ((PropertyStringImpl) result).setValues(values);
              }
              break;
          case URI:
              result = new PropertyUriImpl();
              if (numValues > 0) {
                  List<String> values = new ArrayList<String>();
                  for (int i = 0; i < numValues; i++) {
                      values.add(randomUri());
                  }
                  ((PropertyUriImpl) result).setValues(values);
              }
              break;
          default:
              assert false;
          }
  
          result.setId(randomString());
          result.setDisplayName(randomString());
          result.setLocalName(randomString());
          result.setQueryName(randomString());
  
          return result;
      }
  
      protected PropertyId createIdPropertyData(String id, String value) {
          PropertyIdImpl result = new PropertyIdImpl();
  
          result.setId(id);
          result.setDisplayName(id);
          result.setLocalName(id);
          result.setQueryName(id);
          result.setValue(value);
  
          return result;
      }
  
      protected List<CmisExtensionElement> createExtensions(int depth) {
          List<CmisExtensionElement> result = new ArrayList<CmisExtensionElement>();
  
          String[] namespaces = new String[] { "http://ext1.com", "http://ext2.org", "http://ext3.net" };
  
          for (int i = 0; i < randomInt(4) + 1; i++) {
              String ns = namespaces[randomInt(namespaces.length)];
  
              Map<String, String> attr = new HashMap<String, String>();
              for (int j = 0; j < randomInt(3); j++) {
                  attr.put(randomTag(), randomString());
              }
  
              CmisExtensionElementImpl element;
              if (randomBoolean() || depth < 1) {
                  element = new CmisExtensionElementImpl(ns, randomTag(), attr, randomString());
              } else {
                  element = new CmisExtensionElementImpl(ns, randomTag(), attr, createExtensions(depth - 1));
              }
  
              result.add(element);
          }
  
          return result;
      }
  
      protected String randomString() {
          StringBuilder sb = new StringBuilder();
  
          int length = rnd.nextInt(21) + 3;
          for (int i = 0; i < length; i++) {
              sb.append((char) (rnd.nextInt(94) + 32));
          }
  
          return sb.toString();
      }
  
      protected String randomUri() {
          StringBuilder sb = new StringBuilder("urn:test:");
  
          int length = rnd.nextInt(21) + 3;
          for (int i = 0; i < length; i++) {
              sb.append((char) (rnd.nextInt(26) + 97));
          }
  
          return sb.toString();
      }
  
      protected Boolean randomBoolean() {
          return Boolean.valueOf(rnd.nextBoolean());
      }
  
      protected BigInteger randomInteger() {
          return BigInteger.valueOf(rnd.nextInt());
      }
  
      protected BigDecimal randomDecimal() {
          return BigDecimal.valueOf(rnd.nextDouble() * rnd.nextInt());
      }
  
      protected GregorianCalendar randomDateTime() {
          GregorianCalendar cal = new GregorianCalendar();
          cal.setTimeZone(TimeZone.getTimeZone("GMT" + (rnd.nextBoolean() ? "+" : "-") + (rnd.nextInt(23) - 12) + ":00"));
          cal.set(1583 + rnd.nextInt(8416), rnd.nextInt(12), rnd.nextInt(31) + 1, rnd.nextInt(23), rnd.nextInt(60),
                  rnd.nextInt(60));
          cal.set(Calendar.MILLISECOND, 0);
  
          return cal;
      }
  
      protected int randomInt(int n) {
          return rnd.nextInt(n);
      }
  
      @SuppressWarnings("unchecked")
      protected <T extends Enum<?>> T randomEnum(Class<T> enumClass) {
          T[] values = null;
          try {
              values = (T[]) enumClass.getMethod("values", new Class<?>[0]).invoke(null, new Object[0]);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
          return values[rnd.nextInt(values.length)];
      }
  
      protected String randomTag() {
          StringBuilder sb = new StringBuilder();
  
          int length = rnd.nextInt(7) + 3;
          for (int i = 0; i < length; i++) {
              sb.append((char) (rnd.nextInt(26) + 'a'));
          }
  
          return sb.toString();
      }
  
      /**
       * Compares two data objects.
       */
      protected void assertDataObjectsEquals(String name, Object expected, Object actual, Set<String> ignoreMethods) {
  
          LOG.debug(name + ": " + expected + " / " + actual);
  
          if (expected == null && actual == null) {
              return;
          }
  
          if (expected == null || actual == null) {
              fail("Data object is null! name: " + name + " / expected: " + expected + " / actual: " + actual);
          }
  
          // handle simple types
          if ((expected instanceof String) || (expected instanceof Boolean) || (expected instanceof BigInteger)
                  || (expected instanceof BigDecimal) || (expected instanceof Enum<?>)) {
              assertEquals(name, expected, actual);
  
              return;
          } else if (expected instanceof GregorianCalendar) {
              long expectedMillis = ((GregorianCalendar) expected).getTimeInMillis();
              long actualMillis = ((GregorianCalendar) actual).getTimeInMillis();
  
              assertEquals(name, expectedMillis, actualMillis);
  
              return;
          } else if (expected instanceof List<?>) {
              List<?> expectedList = (List<?>) expected;
              List<?> actualList = (List<?>) actual;
  
              assertEquals(name + ".length", expectedList.size(), actualList.size());
  
              for (int i = 0; i < expectedList.size(); i++) {
                  assertDataObjectsEquals(name + "[" + i + "]", expectedList.get(i), actualList.get(i), ignoreMethods);
              }
  
              return;
          } else if (expected instanceof Map<?, ?>) {
              Map<?, ?> expectedMap = (Map<?, ?>) expected;
              Map<?, ?> actualMap = (Map<?, ?>) actual;
  
              assertEquals(name + ".length", expectedMap.size(), actualMap.size());
  
              for (Map.Entry<?, ?> entry : expectedMap.entrySet()) {
                  assertTrue(name + ".contains[" + entry.getKey() + "]", actualMap.containsKey(entry.getKey()));
                  assertDataObjectsEquals(name + "[" + entry.getKey() + "]", entry.getValue(),
                          actualMap.get(entry.getKey()), ignoreMethods);
              }
  
              return;
          } else if (expected instanceof Set<?>) {
              Set<?> expectedSet = (Set<?>) expected;
              Set<?> actualSet = (Set<?>) actual;
  
              assertEquals(name + ".length", expectedSet.size(), actualSet.size());
  
              for (Object item : expectedSet) {
                  assertTrue(name + ".contains[" + item + "]", actualSet.contains(item));
              }
  
              return;
          }
  
          for (Method m : expected.getClass().getMethods()) {
              if (!m.getName().startsWith("get") && !m.getName().startsWith("is") && !m.getName().startsWith("supports")) {
                  continue;
              }
  
              if (ignoreMethods != null && ignoreMethods.contains(m.getName())) {
                  continue;
              }
  
              if (m.getName().equals("getClass")) {
                  continue;
              }
  
              if (m.getParameterTypes().length != 0) {
                  continue;
              }
  
              try {
                  Object expectedValue = m.invoke(expected, new Object[0]);
                  Object actualValue = m.invoke(actual, new Object[0]);
  
                  assertDataObjectsEquals(name + "." + m.getName(), expectedValue, actualValue, ignoreMethods);
              } catch (Exception e) {
                  fail(e.toString());
              }
          }
      }
  
      /**
       * Compares two extensions.
       */
      protected void assertExtensionsEquals(List<CmisExtensionElement> expected, List<CmisExtensionElement> actual) {
          if (expected == null && actual == null) {
              return;
          }
  
          if (expected == null || actual == null) {
              fail("Extension list is null! expected: " + expected + " / actual: " + actual);
          }
  
          assertEquals(expected.size(), actual.size());
  
          int n = expected.size();
          for (int i = 0; i < n; i++) {
              CmisExtensionElement expectedElement = expected.get(0);
              CmisExtensionElement actualElement = actual.get(0);
  
              if (expectedElement == null || actualElement == null) {
                  fail("Extension element is null! expected: " + expectedElement + " / actual: " + actualElement);
              }
  
              assertEquals(expectedElement.getName(), actualElement.getName());
              assertEquals(expectedElement.getValue(), actualElement.getValue());
              assertExtensionsEquals(expectedElement.getChildren(), actualElement.getChildren());
          }
      }
  }
  