@Override
 protected ISpecialCharm getSpecialCharm(String charmId) {
   for (ISpecialCharm special : configuration.getSpecialCharms()) {
     if (special.getCharmId().equals(charmId)) {
       return special;
     }
   }
   return new NullSpecialCharm();
 }
 private void initSpecialCharmConfigurations() {
   ICharmIdMap charmIdMap = getCharmIdMap();
   ISpecialCharm[] specialCharms = getSpecialCharms();
   for (ISpecialCharm specialCharm : specialCharms) {
     ICharm charm = charmIdMap.getCharmById(specialCharm.getCharmId());
     if (charm == null) {
       continue;
     }
     ILearningCharmGroup group = getGroupById(charm.getCharacterType(), charm.getGroupId());
     manager.registerSpecialCharmConfiguration(specialCharm, charm, group);
   }
 }
 private IPrerequisiteModifyingCharm[] getPrerequisiteModifyingCharms() {
   if (prerequisiteModifyingCharms == null) {
     List<IPrerequisiteModifyingCharm> charms = new ArrayList<IPrerequisiteModifyingCharm>();
     for (ISpecialCharm charm : getSpecialCharms())
       // assuming all of these are native for now
       if (charm instanceof IPrerequisiteModifyingCharm
           && getCharmIdMap().getCharmById(charm.getCharmId()).getCharacterType()
               == context.getBasicCharacterContext().getCharacterType())
         charms.add((IPrerequisiteModifyingCharm) charm);
     prerequisiteModifyingCharms = new IPrerequisiteModifyingCharm[charms.size()];
     charms.toArray(prerequisiteModifyingCharms);
   }
   return prerequisiteModifyingCharms;
 }
  public final boolean isLearnable(ICharm charm) {
    if (isAlienCharm(charm)) {
      ICasteType casteType = context.getBasicCharacterContext().getCasteType();
      if (!getCharmTemplate(getNativeCharacterType()).isAllowedAlienCharms(casteType)) {
        return false;
      }
      if (charm.hasAttribute(ICharmData.NOT_ALIEN_LEARNABLE)) {
        return false;
      }
    }
    if (charm.isBlockedByAlternative(context.getMagicCollection())) {
      return false;
    }
    if (MartialArtsUtilities.isMartialArtsCharm(charm)) {
      boolean isSiderealFormCharm =
          MartialArtsUtilities.isFormCharm(charm)
              && MartialArtsUtilities.hasLevel(MartialArtsLevel.Sidereal, charm);
      if (isSiderealFormCharm
          && !arbitrator.isCelestialMartialArtsGroupCompleted(getMartialArtsGroups())) {
        return false;
      }
      if (!getCharmTemplate(getNativeCharacterType())
          .getMartialArtsRules()
          .isCharmAllowed(
              charm,
              context.getCharmContext().getCharmConfiguration(),
              context.getBasicCharacterContext().isExperienced())) {
        return false;
      }
    }
    for (ICharmAttributeRequirement requirement : charm.getAttributeRequirements()) {
      if (!requirement.isFulfilled(getLearnedCharms(true))) {
        return false;
      }
    }
    for (IGenericTrait prerequisite : charm.getPrerequisites()) {
      IGenericTrait prerequisiteTrait =
          context.getTraitCollection().getTrait(prerequisite.getType());
      int prereq = prerequisite.getCurrentValue();
      for (ISpecialCharm specialCharm : getPrerequisiteModifyingCharms())
        if (specialCharm instanceof IPrerequisiteModifyingCharm
            && isLearned(specialCharm.getCharmId()))
          prereq =
              ((IPrerequisiteModifyingCharm) specialCharm)
                  .getTraitModifier(charm, prerequisiteTrait.getType(), prereq);

      if (prerequisiteTrait == null || prereq > prerequisiteTrait.getCurrentValue()) {
        return false;
      }
    }
    IGenericTrait essenceTrait = context.getTraitCollection().getTrait(OtherTraitType.Essence);
    int essencePrereq = charm.getEssence().getCurrentValue();
    for (ISpecialCharm specialCharm : getPrerequisiteModifyingCharms())
      if (specialCharm instanceof IPrerequisiteModifyingCharm
          && isLearned(specialCharm.getCharmId()))
        essencePrereq =
            ((IPrerequisiteModifyingCharm) specialCharm)
                .getTraitModifier(charm, OtherTraitType.Essence, essencePrereq);
    if (essencePrereq > essenceTrait.getCurrentValue()) {
      return false;
    }
    for (ICharm parentCharm : charm.getLearnPrerequisitesCharms(this)) {
      if (!isLearnable(parentCharm)) {
        return false;
      }
    }
    return true;
  }
 @Override
 public void buildFor(ISpecialCharm charm) {
   charm.accept(new SwingSpecialCharmVisitor());
 }
 @SuppressWarnings("unchecked")
 private <T> T getModelFromCharm(ISpecialCharm visitedCharm) {
   return (T) configuration.getSpecialCharmConfiguration(visitedCharm.getCharmId());
 }