Andrey: I'll be happy to write a Wiki documentation page, but probably
won't have time for a while.
An example, using the Dice class:
-----
Dice.java:
-----
public class Dice {
// exactly the same as on the Documentation page, except:
public static Construct getConstruct() {
return new ConstructDice();
}
public static Represent getRepresent() {
return new RepresentDice();
}
}
class RepresentDice implements Represent {
public Node representData(Representer parent, Object data) {
Dice dice = (Dice) data;
String value = dice.getA() + "d" + dice.getB();
return parent.representScalar(new Tag("!dice"), value);
}
}
class ConstructDice extends AbstractConstruct {
public Object construct(Constructor parent, Node node) {
String val = (String) parent.constructScalar(node);
int position = val.indexOf('d');
Integer a = Integer.parseInt(val.substring(0, position));
Integer b = Integer.parseInt(val.substring(position + 1));
return new Dice(a, b);
}
}
-----
The difference is subtle, but important. Now it is easy to create a
Constructor that can construct many kinds of objects with custom
Construct implementations. For example:
Dice: "2d8+6"
Distance: "70 ft." or "500 metres"
Time: "200 seconds" or "3 years"
Before this change, we might have:
DiceConstructor extends Constructor
DistanceConstructor extends DiceConstructor
TimeConstructor extends DistanceConstructor
which I think we can all agree is very messy.
If we knew all the types we wanted to do this for, then we could
create a Constructor that knows about each of them
(EverythingConstructor). However, EverythingConstructor then contains
data about Dice, Distances, Times, and so forth. It would be very easy
for that file to become large and complex, and changing any part of
the Dice, Distance or Time classes would require changing
EverythingConstructor too.
Now we use:
-----
YamlConfig config = YamlConfig.createDefault();
config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!dice"), Dice.getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Dice.class, Dice.getRepresent() );
config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!distance"), Distance.getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Distance.class, Distance.getRepresent() );
config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!time"), Time.getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Time.class, Time.getRepresent() );
-----
This creates a YamlConfig that knows how to use the custom Construct /
Represent instance for each of those types.
-----
If we wanted to make it even easier, we could use:
-----
Dice.java:
-----
public class Dice {
// exactly the same as above, except:
public static void registerConfig ( YamlConfig config ) {
config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!dice"), getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Dice.class, getRepresent() );
}
}
-----
Then creating the correct YamlConfig is just:
-----
YamlConfig config = YamlConfig.createDefault();
Dice.registerConfig ( config );
Distance.registerConfig ( config );
Time.registerConfig ( config );
-----
Then, to change the Construct or Represent for Distance, you look in
the Distance.java file.
Most users should not have to touch the Constructor or Representer
classes. They are complex and have a large learning curve. It is still
possible to extend Constructor or Representer (see the
CompactConstructor class, which still works this way), but usually
unnecessary.
/Jordan