API для обозначения токенизации файлов, установки значения объекта и перечисления позиций токенов

В моем приложении для Android мне нужно прочитать большой объем данных из набора файлов .ini, которые изначально (и до сих пор) были развернуты с помощью приложения Windows. Приложение предназначено для конкретного аппаратного обеспечения, а основная цель файлов .ini - описать константы программирования, данные в реальном времени и элементы пользовательского интерфейса в отношении аппаратного устройства.

Чтобы перейти к конкретному вопросу проектирования Java, я хотел бы получить помощь, вот пример того, как будет структурирована типичная строка данных из .ini файла. Обычно у нас есть имя параметра, за которым следуют различные столбцы строковых или числовых данных:

example = example,  "example",  "C",  0,   255,  -1,    -1,  256,  256 

Каждая строка данных, которые я анализирую и токенизу, затем представляется в приложении как объект. Чтобы выполнить создание каждого объекта из каждой строки данных файла .ini, я в настоящее время использую интерфейс Builder/Fluent. Таким образом, вышеприведенная строка приведет к созданию объекта таким образом:

someObject.setName( t.s(2) ).setUnits( t.s(3) ).setLowerUpper( t.f(4), t.f(5) ) ...

t - это мой экземпляр класса tokenizer, который использовался для анализа файла .ini, а его методы s(int) и f(int) - это получатели, которые извлекают целое число или float для данного номера столбца.

Это немного беспорядочно, потому что мой основной класс обработки файлов .ini, который выполняет создание объекта на основе данных, возвращаемых из токенизатора, должен содержать магические числа повсюду для столбцов строк. Я также должен позаботиться о том, чтобы использовать s() или f(), если это необходимо (хотя компилятор, конечно, сообщит об ошибке, если я использую неверный, на основе типов, необходимых для моих объектов).

Мне бы очень хотелось, чтобы у меня был другой класс, который я мог бы назвать IniFileDefinitions. В идеале этот класс будет содержать перечисляемые определения для номеров строк и связанных с ними типов. Поэтому, используя этот класс, созданное выше создание объекта может выглядеть примерно так (псевдокод, очевидно):

someObject.setName( t.get(INI_NAME) ).setUnits( t.get(INI_UNITS) ).setLowerUpper( t.get(INI_LOWER), t.get(INI_UPPER) );

Где я немного застрял, так это то, как я могу определить значение перечисления, которое я мог бы передать моему токенизатору, и чтобы токенизатор не только использовал это перечисление для определения номера столбца, но и изменял возвращаемый тип ( целое число, строка, float и т.д.) на основе этого перечисления.

Я понимаю, что перечисления Java достаточно мощные, и для меня было бы возможно иметь два поля для перечисления, где первое поле дает номер строки, а второе поле описывает тип, например:

public enum someIniEnumDefs{
   INI_NAME( 2, INI_TYPE_STRING )
   ...

Мне пришло в голову, что способ сделать эту работу будет иметь метод getter в моем токенизаторе, который может варьировать тип, который он возвращает, в зависимости от того, какой тип перечисления должен указывать, но, очевидно, это невозможно в Java, Насколько мне известно, мне пришлось бы возвращать пользовательский объект, содержащий один из всех типов (string, integer,...) или просто вернуть Object (ugh). Мне не особенно нравится любое из этих решений.

Одна из моих идей заключалась в том, что токенизатор может иметь несколько (перегруженных) значений get() методов, каждый метод возвращает другой примитивный тип. Какой конкретный метод get() будет вызываться, может зависеть, как-то, от моего "типа" поля в определениях перечислений. Я не думаю, что я мог бы это сделать. Единственный способ, по которому я думал, состоял бы в том, чтобы разделить мои перечисления на несколько групп, представляющих разные типы, а затем мои методы tokenizer get будут перегружены следующим образом:

int get(SomeIniEnumDefs_Ints){ ... return someInt }
String get(SomeIniEnumDefs_Strings){ ... return someString }
...

Учитывая мой недостаток опыта работы с перечислениями Java (которые, как я знаю, далеки, гораздо более мощные, чем то, к чему я привык в простой старой C!), я знаю, что должен быть аккуратный способ добиться этого. Может ли кто-нибудь предложить какие-либо дополнительные указания?

Спасибо,

Трев

Ответ 1

То, что вы пытаетесь сделать, невозможно с перечислениями Java. Однако это можно было бы легко достичь по-другому:

public class IniParam<T> {

  public static final IniParam<String> NAME = new IniParam<String>(2);

  public static final IniParam<String> UNITS = new IniParam<String>(3);

  public static final IniParam<Integer> LOWER = new IniParam<Integer>(4);

  public static final IniParam<Integer> UPPER = new IniParam<Integer>(5);

  private final int position;

  private IniParam(int position) {
    this.position = position;
  }

  public int getPosition() {
    return position;
  }    
}

Tokenizer тогда может выглядеть так:

public class Tokenizer {

  public String get(IniParam<String> iniParam) {
    int position = iniParam.getPosition();
    //...
    return "some string from .ini";
  }

  public int get(IniParam<Integer> iniParam) {
    // ... 
    // return some integer from .ini
  }
}

Пример использования:

    Tokenizer t = new Tokenizer();
    String name = t.get(IniParam.NAME);
    int lower = t.get(IniParam.LOWER);
    someObject.setName( t.get(IniParam.NAME) ).setUnits( t.get(IniParam.UNITS) ).setLowerUpper( t.get(IniParam.LOWER), t.get(IniParam.UPPER) );

 

UPDATE

К сожалению, класс Tokenizer, указанный выше, не будет компилироваться с помощью компиляторов JDK 7/Eclipse 3.6+ (я не могу проверить его сам прямо сейчас, но исправления для следующих Oracle и Eclipse ошибки ошибок компиляции в методах get(...)). Если вы столкнулись с этой проблемой, это обходной путь:

public class IniParam<T> {

  public static final IniParam<String> NAME = new IniParam<String>(2, String.class);

  public static final IniParam<String> UNITS = new IniParam<String>(3, String.class);

  public static final IniParam<Integer> LOWER = new IniParam<Integer>(4, Integer.class);

  public static final IniParam<Integer> UPPER = new IniParam<Integer>(5, Integer.class);

  private final int position;

  private final Class<? extends T> type;

  private IniParam(int position, Class<? extends T> type) {
    this.position = position;
    this.type = type;
  }

  public int getPosition() {
    return position;
  }

  public Class<? extends T> getType() {
    return type;
  }
}


public class Tokenizer {

  public <T> T get(IniParam<T> iniParam) {
    int position = iniParam.getPosition();
    Class<? extends T> type = iniParam.getType();
    if (type == String.class) {
      //...
      return type.cast("some string from .ini");        
    } else if (type == Integer.class) {
      //...
      // Integer result = ...;
      return type.cast(result);
    } else {
      throw new IllegalArgumentException("Unexpected IniParam type: " + type);
    }      
  }
}