Разработка API в отношении токенизации файлов, установки значений объектов и перечислений позиций токенов - PullRequest
1 голос
/ 10 июля 2011

В моем приложении для 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 - это мой экземпляр класса токенизатора, который использовался для разбора файла .ini, а его методы s(int) и f(int) являются геттерами, которые выбирают целое число или число с плавающей запятой для данного номера столбца.

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

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

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

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

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

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

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

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

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

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

Спасибо,

Trev

1 Ответ

1 голос
/ 10 июля 2011

То, что вы пытаетесь сделать, невозможно с помощью перечислений 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+ (прямо сейчас я не могу проверить это, но исправлены следующие * Eclipse Oracle и ошибки предполагают ошибки компиляции в 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);
    }      
  }
}
...