Package ch.agent.crnickl.impl

Source Code of ch.agent.crnickl.impl.ValueTypeImpl$BooleanScanner

/*
*   Copyright 2012 Hauser Olsson GmbH
*
* 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: ch.agent.crnickl.impl
* Type: ValueTypeImpl 
* Version: 1.0.0
*/
package ch.agent.crnickl.impl;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ch.agent.crnickl.T2DBException;
import ch.agent.crnickl.T2DBMsg;
import ch.agent.crnickl.T2DBMsg.D;
import ch.agent.crnickl.api.Database;
import ch.agent.crnickl.api.NamingPolicy;
import ch.agent.crnickl.api.Surrogate;
import ch.agent.crnickl.api.UpdatableValueType;
import ch.agent.crnickl.api.ValueScanner;
import ch.agent.crnickl.api.ValueType;
import ch.agent.t2.time.DateTime;
import ch.agent.t2.time.Day;
import ch.agent.t2.time.TimeDomain;
import ch.agent.t2.time.TimeDomainFactory;
import ch.agent.t2.time.TimeDomainManager;

/**
* Default implementation of {@link ValueType}.
* In agreement with the convention mentioned in the {@link ValueScanner}
* class comment, the value type is passed to value scanner constructors
* when such constructors are available.
*
* @author Jean-Paul Vetterli
* @version 1.0.0
* @param <T> the underlying data type of the values
*/
public class ValueTypeImpl<T> extends DBObjectImpl implements ValueType<T> {
 
  private static final int MAX_NAME_LENGTH = 25;
  private static final String NAME_PATTERN = "[A-Za-z0-9_][A-Za-z0-9_,\\-]*";
 
  /**
   * A value scanner for boolean data. The scanner parses strings as
   * {@link java.lang.Boolean}. The string "true" (case insensitive) is
   * interpreted as true, all others as false.
   */
  public static class BooleanScanner implements ValueScanner<Boolean> {
   
    @Override
    public Class<Boolean> getType() {
      return Boolean.class;
    }

    @Override
    public void check(Boolean value) {
    }

    @Override
    public Boolean scan(String value) throws T2DBException {
      return new Boolean(value);
    }

    @Override
    public String toString(Boolean value) {
      return value == null ? null : value.toString();
    }
   
  }
 
  /**
   * A value scanner for numeric data. The scanner parses strings as
   * {@link java.lang.Double}. A hyphen ("-") is interpreted as Double.NaN.
   */
  public static class NumberScanner implements ValueScanner<Double> {
   
    @Override
    public Class<Double> getType() {
      return Double.class;
    }

    @Override
    public void check(Double value) {
    }

    @Override
    public Double scan(String value) throws T2DBException {
      try {
        return new Double(value);
      } catch (NumberFormatException e) {
        if (value != null && value.equals("-"))
          return Double.NaN;
        throw T2DBMsg.exception(D.D10103, value);
      }
    }

    @Override
    public String toString(Double value) {
      return value == null ? null : value.toString();
    }
   
  }
 
  /**
   * A value scanner for textual data.
   * After considering the alternatives,
   * the design committee has decided to implement
   * textual data with {@link java.lang.String}.
   */
  public static class TextScanner implements ValueScanner<String> {

    @Override
    public Class<String> getType() {
      return String.class;
    }

    @Override
    public void check(String value) {
    }

    @Override
    public String scan(String value) {
      return value;
    }

    @Override
    public String toString(String value) {
      return value;
    }
   
  }
 
  /**
   * A value scanner for textual data representing simple names.
   */
  public static class NameScanner implements ValueScanner<String> {

    private Matcher validNameMatcher;
   
    public NameScanner() {
      validNameMatcher = Pattern.compile(NAME_PATTERN).matcher("");
    }

    @Override
    public Class<String> getType() {
      return String.class;
    }

    @Override
    public void check(String value) throws T2DBException {
      if (value == null || value.length() == || value.length() > MAX_NAME_LENGTH)
        throw T2DBMsg.exception(D.D10105, value, 0, MAX_NAME_LENGTH);
      validNameMatcher.reset(value);
      if (!validNameMatcher.matches())
        throw T2DBMsg.exception(D.D10104, value, NAME_PATTERN);
    }

    @Override
    public String scan(String value) throws T2DBException {
      check(value);
      return value;
    }

    @Override
    public String toString(String value) throws T2DBException {
      check(value);
      return value;
    }
  }
 
  /**
   * A value scanner for data representing {@link TimeDomain}s.
   */
  public static class TimeDomainScanner implements ValueScanner<TimeDomain> {

    private TimeDomainFactory factory;
   
    public TimeDomainScanner() {
      factory = TimeDomainManager.getFactory();
    }

    @Override
    public Class<TimeDomain> getType() {
      return TimeDomain.class;
    }

    @Override
    public void check(TimeDomain value) {
    }

    @Override
    public TimeDomain scan(String value) throws T2DBException {
      try {
        return factory.get(value);
      } catch (Exception e) {
        throw T2DBMsg.exception(e, D.D10107, value, ValueType.class.getName());
      }
    }

    @Override
    public String toString(TimeDomain value) {
      return value == null ? null : value.getLabel();
    }
   
  }
 
  /**
   * A value scanner for data representing {@link Day}s.
   */
  public static class DateScanner implements ValueScanner<Day> {

    @Override
    public Class<Day> getType() {
      return Day.class;
    }

    @Override
    public void check(Day value) {
    }

    @Override
    public Day scan(String value) throws T2DBException {
      try {
        return new Day(value);
      } catch (Exception e) {
        throw T2DBMsg.exception(e, D.D10107, value, ValueType.class.getName());
      }
    }

    @Override
    public String toString(Day value) {
      return value == null ? null : value.toString();
    }
   
  }
 
  /**
   * A value scanner for data representing {@link DateTime}s.
   */
  public static class DateTimeScanner implements ValueScanner<DateTime> {

    @Override
    public Class<DateTime> getType() {
      return DateTime.class;
    }

    @Override
    public void check(DateTime value) {
    }

    @Override
    public DateTime scan(String value) throws T2DBException {
      try {
        return new DateTime(value);
      } catch (Exception e) {
        throw T2DBMsg.exception(e, D.D10107, value, ValueType.class.getName());
      }
    }

    @Override
    public String toString(DateTime value) {
      return value == null ? null : value.toString();
    }
   
  }
 
  /**
   * A value scanner for data representing {@link ValueType}s.
   */
  @SuppressWarnings("rawtypes")
  public static class TypeScanner implements ValueScanner<ValueType> {

    private Database database;
   
    public TypeScanner(Database database) {
      this.database = database;
    }

    @Override
    public Class<ValueType> getType() {
      return ValueType.class;
    }

    @Override
    public void check(ValueType value) {
    }

    @Override
    public ValueType<?> scan(String value) throws T2DBException {
      try {
        return database.getValueType(value);
      } catch (Exception e) {
        throw T2DBMsg.exception(e, D.D10107, value, ValueType.class.getName());
      }
    }

    @Override
    public String toString(ValueType value) {
      return value == null ? null : value.getName();
    }
   
  }
 
  private ValueScanner<T> scanner;
  private ValueAccessMethods<T> am; // can be null, but getting throws an exception
  private String name;
  private boolean restricted;
  private Map<T, String> values;
  private StandardValueType keyword; // null means custom scanner

  /**
   * This constructor is intended for {@link UpdatableValueTypeImpl} when implementing
   * the {@link #edit} method.
   *
   * @param valueType
   * @throws T2DBException
   */
  protected ValueTypeImpl(ValueTypeImpl<T> valueType) throws T2DBException {
    super(valueType.getSurrogate());
    this.scanner = valueType.scanner;
    this.am = valueType.am;
    this.name = valueType.name;
    this.restricted = valueType.restricted;
    this.values = valueType.values;
    this.keyword = valueType.keyword;
  }

  /**
   * Construct a {@link ValueType}.
   *
   * @param name a string
   * @param restricted if true the value type will keep a list of allowed values
   * @param scannerClassOrKeyword a keyword identifying a standard value type of the name of a scanner class
   * @param valuesAndDescriptions a map of allowed values (as strings) and their description
   * @param surrogate a surrogate
   * @throws T2DBException
   */
  public ValueTypeImpl(String name, boolean restricted, String scannerClassOrKeyword,
      Map<String, String> valuesAndDescriptions, Surrogate surrogate) throws T2DBException {
    super(surrogate);
    this.name = name;
    this.restricted = restricted;
    this.scanner = findStandardScanner(scannerClassOrKeyword);
    setValues(valuesAndDescriptions);
    am = getDatabase().getAccessMethods(this);
  }

  /**
   * Refresh state.
   *
   * @throws T2DBException
   */
  protected void update() throws T2DBException {
    this.name = getName();
    ValueType<T> vt = getDatabase().getValueType(getSurrogate());
    // load all values again, it's expected to be a rare operation
    values = vt.getValueDescriptions();
  }
 
  @SuppressWarnings("unchecked")
  private void setValues(Map<String, String> valuesAndDescriptions) throws T2DBException {
    if (valuesAndDescriptions == null || valuesAndDescriptions.size() == 0)
      this.values = new HashMap<T, String>();
    else {
      if (!isRestricted())
        throw T2DBMsg.exception(D.D10108, getName());
      if (getType().equals(String.class))
        this.values = new LinkedHashMap<T, String>((Map<T, String>)valuesAndDescriptions);
      else {
        this.values = new LinkedHashMap<T, String>();
        for (Map.Entry<String, String> e : valuesAndDescriptions.entrySet()) {
          /*
           * Don't use this.scan at this point, check will fail
           * because the value is not yet in the list of valid values.
           * On the other hand, the value must be valid, so catch scan
           * failures. Such failures can occur with bad bootstrap data
           * for example.
           */
          T value = this.scanner.scan(e.getKey());
          this.values.put(value, e.getValue());
        }
      }
    }
  }
 
  @SuppressWarnings("unchecked")
  private ValueScanner<T> findStandardScanner(StandardValueType type) throws T2DBException {
    ValueScanner<T> sc = null;
    switch (type) {
    case BOOLEAN:
      sc = scanner = (ValueScanner<T>) new BooleanScanner();
      break;
    case DATE:
      sc = scanner = (ValueScanner<T>) new DateScanner();
      break;
    case DATETIME:
      sc = scanner = (ValueScanner<T>) new DateTimeScanner();
      break;
    case NAME:
      sc = scanner = (ValueScanner<T>) new NameScanner();
      break;
    case NUMBER:
      sc = scanner = (ValueScanner<T>) new NumberScanner();
      break;
    case TEXT:
      sc = scanner = (ValueScanner<T>) new TextScanner();
      break;
    case TIMEDOMAIN:
      sc = scanner = (ValueScanner<T>) new TimeDomainScanner();
      break;
    case TYPE:
      sc = scanner = (ValueScanner<T>) new TypeScanner(getSurrogate().getDatabase());
      break;
    default:
      throw new RuntimeException(type.name());
    }
    keyword = type;
    return sc;
  }
 
  private ValueScanner<T> findStandardScanner(String scannerClassOrKeyword) throws T2DBException {
    StandardValueType type = null;
    ValueScanner<T> scanner= null;
    try {
      type = StandardValueType.valueOf(scannerClassOrKeyword);
    } catch (IllegalArgumentException e) {}
   
    if (type == null) {
      keyword = null;
      try {
        @SuppressWarnings("unchecked")
        Class<ValueScanner<T>> scannerClass = (Class<ValueScanner<T>>) Class.forName(scannerClassOrKeyword);
        Constructor<ValueScanner<T>> constructor = scannerClass.getConstructor(ValueType.class);
        if (constructor == null)
          scanner = scannerClass.newInstance();
        else
          scanner = constructor.newInstance(this);
      } catch (Exception e) {
        throw T2DBMsg.exception(e, D.D10110, name, scannerClassOrKeyword);
      }
    } else
      scanner = findStandardScanner(type);
    return scanner;
  }
 
  @Override
  public UpdatableValueType<T> edit() {
    try {
      return new UpdatableValueTypeImpl<T>(this);
    } catch (T2DBException e) {
      throw new RuntimeException("bug", e);
    }
  }
 
  /**
   * Return the value access methods object for this type.
   * The value type object plays the role of a cache for
   * the value access method object, which is expensive to
   * construct.
   *
   * @return a value access methods object
   */
  public <S>ValueAccessMethods<T> getAccessMethods() throws T2DBException {
    if (am == null)
      throw T2DBMsg.exception(D.D10102, toString());
    return am;
  }
 
  @Override
  public String getName() {
    return name;
  }
 
  @Override
  public boolean isRestricted() {
    return restricted;
  }

  @Override
  public boolean isBuiltIn() {
    return ((SurrogateImpl) getSurrogate()).getId() <= DatabaseBackend.MAX_MAGIC_NR;
  }

  @Override
  public Class<T> getType() {
    return scanner.getType();
  }
 
  @Override
  public ValueScanner<T> getScanner() {
    return scanner;
  }

  @Override
  public Set<T> getValues() {
    return values.keySet();
  }
 
  @Override
  public Map<T, String> getValueDescriptions() {
    return values;
  }

  @Override
  public boolean isCompatible(Object obj) {
    return getType().isInstance(obj);
  }

  @Override
  public T scan(String value) throws T2DBException {
    T result = null;
    if (value != null && value.length() == 0 && !isRestricted())
      value = null;
    if (value != null) {
      result = scanner.scan(value);
      if (result == null) {
        // interpret value "" as null, except for String
        if (value.length() != 0)
          throw T2DBMsg.exception(D.D10114, value, getName());
      } else
        check(result);
    }
    return result;
  }

  @Override
  public void check(T value) throws T2DBException {
    if (isRestricted() && !values.containsKey(value))
      throw T2DBMsg.exception(D.D10115, value, getName());
  }

  @SuppressWarnings("unchecked")
  @Override
  public String toString(Object value) throws T2DBException {
    String result = null;
    try {
      if (!isRestricted() || values.containsKey(value))
        result = scanner.toString((T)value);
    } catch (Exception e) {
      throw T2DBMsg.exception(e, D.D10114, value, getName());
    }
    return result;
  }
 
  @Override
  public Collection<String> getValues(String format) {
    NamingPolicy namingPolicy = getDatabase().getNamingPolicy();
    try {
      Map<T, String> map = getValueDescriptions();
      List<String> list = new ArrayList<String>(map.size());
      for (Map.Entry<T, String> e : map.entrySet()) {
        String value = toString(e.getKey());
        String descr = e.getValue();
        if (descr != null && descr.length() > 0) {
          if (format == null)
            value = namingPolicy.joinValueAndDescription(value, descr);
          else
            value = String.format(format, value, descr);
        }
        list.add(value);
      }
      Collections.sort(list);
      return list;
    } catch (T2DBException e) {
      throw new RuntimeException("bug", e);
    }
  }

  @Override
  public String toString() {
    return getName();
  }
 
  @Override
  public String getExternalRepresentation() {
    if (keyword == null)
      return scanner.getClass().getName();
    else
      return keyword.name();
  }
 
  @Override
  public StandardValueType getStandardValueType() {
    return keyword;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <S> ValueType<S> typeCheck(Class<S> type) throws T2DBException {
    try {
      if (type.isAssignableFrom(getScanner().getType()))
        return (ValueType<S>) this;
    } catch (Exception e) {
    }
    throw T2DBMsg.exception(D.D10101, getName(), type.getName(), getScanner().getType().getName());
  }

}
TOP

Related Classes of ch.agent.crnickl.impl.ValueTypeImpl$BooleanScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.