@Override
 public double setDouble(String sName, double dValue) throws OmDeveloperException {
   Double dOldValue = null;
   StandardQuestion sq = (StandardQuestion) getQuestion();
   if (sq.isPlaceholdersInitialized() && placeholders.containsKey(sName)) {
     dOldValue = new Double(getDouble(sName));
   }
   double dOldAux = super.setDouble(sName, dValue);
   placeholders.remove(sName);
   return dOldValue == null ? dOldAux : dOldValue.doubleValue();
 }
 @Override
 public boolean setBoolean(String sName, boolean bValue) throws OmDeveloperException {
   Boolean bOldValue = null;
   StandardQuestion sq = (StandardQuestion) getQuestion();
   if (sq.isPlaceholdersInitialized() && placeholders.containsKey(sName)) {
     bOldValue = new Boolean(getBoolean(sName));
   }
   boolean bOldAux = super.setBoolean(sName, bValue);
   placeholders.remove(sName);
   return bOldValue == null ? bOldAux : bOldValue.booleanValue();
 }
 @Override
 public int setInteger(String sName, int iValue) throws OmDeveloperException {
   Integer iOldValue = null;
   StandardQuestion sq = (StandardQuestion) getQuestion();
   if (sq.isPlaceholdersInitialized() && placeholders.containsKey(sName)) {
     iOldValue = new Integer(getInteger(sName));
   }
   int iOldAux = super.setInteger(sName, iValue);
   placeholders.remove(sName);
   return iOldValue == null ? iOldAux : iOldValue.intValue();
 }
 @Override
 public String setString(String sName, String sValue) throws OmDeveloperException {
   boolean isOldValueNull = false;
   String sOldValue = null;
   StandardQuestion sq = (StandardQuestion) getQuestion();
   if (sq.isPlaceholdersInitialized() && placeholders.containsKey(sName)) {
     sOldValue = getString(sName);
     isOldValueNull = sOldValue == null;
   }
   String sOldAux = super.setString(sName, sValue);
   placeholders.remove(sName);
   return isOldValueNull ? null : sOldValue == null ? sOldAux : sOldValue;
 }
  @Override
  public boolean getBoolean(String sName) throws OmDeveloperException {
    boolean bValue = false;
    String placeholder = placeholders.get(sName);
    if (placeholder == null) {
      bValue = super.getBoolean(sName);
    } else {
      StandardQuestion sq = (StandardQuestion) getQuestion();
      if (sq.isPlaceholdersInitialized()) {
        String placeholderReplaced = sq.applyPlaceholders(placeholder);
        if (placeholderReplaced.equals("yes")) {
          bValue = true;
        } else if (placeholderReplaced.equals("no")) {
          bValue = false;
        } else {
          StringBuffer error = new StringBuffer();
          error.append('<');
          error.append(getTagName());
          error.append(">: property '");
          error.append(sName);
          error.append("' must be either 'yes' or 'no'");
          throw new OmFormatException(error.toString());
        }
      } else {
        StringBuffer error = new StringBuffer();
        error.append('<');
        error.append(getTagName());
        error.append(">: Placeholder ");
        error.append(placeholder);
        error.append(" for property '");
        error.append(sName);
        error.append("' can not be replaced because placeholders have not been initialized");
        throw new OmFormatException(error.toString());
      }

      // Do specific check if defined
      if (checks.containsKey(sName)) {
        checks.get(sName).check(new Boolean(bValue));
      }
    }
    return bValue;
  }
  @Override
  public double getDouble(String sName) throws OmDeveloperException {
    double dValue = 0.0;
    String placeholder = placeholders.get(sName);
    if (placeholder == null) {
      dValue = super.getDouble(sName);
    } else {
      StandardQuestion sq = (StandardQuestion) getQuestion();
      if (sq.isPlaceholdersInitialized()) {
        String placeholderReplaced = sq.applyPlaceholders(placeholder);
        try {
          dValue = Double.parseDouble(placeholderReplaced);
        } catch (NumberFormatException e) {
          StringBuffer error = new StringBuffer();
          error.append('<');
          error.append(getTagName());
          error.append(">: property '");
          error.append(sName);
          error.append("' is not a valid double");
          throw new OmFormatException(error.toString());
        }
      } else {
        StringBuffer error = new StringBuffer();
        error.append('<');
        error.append(getTagName());
        error.append(">: Placeholder ");
        error.append(placeholder);
        error.append(" for property '");
        error.append(sName);
        error.append("' can not be replaced because placeholders have not been initialized");
        throw new OmFormatException(error.toString());
      }

      // Do specific check if defined
      if (checks.containsKey(sName)) {
        checks.get(sName).check(new Double(dValue));
      }
    }
    return dValue;
  }
  @Override
  public String getString(String sName) throws OmDeveloperException {
    String sValue = null;
    String placeholder = placeholders.get(sName);
    if (placeholder == null) {
      sValue = super.getString(sName);
    } else {
      StandardQuestion sq = (StandardQuestion) getQuestion();
      if (sq.isPlaceholdersInitialized()) {
        sValue = sq.applyPlaceholders(placeholder);
      } else {
        sValue = placeholder;
      }

      // Check properties with restrictions
      if (PROPERTIES_TO_INITIALIZE_PLACEHOLDERS_WITH_RESTRICTIONS.containsKey(sName)) {
        String restriction = PROPERTIES_TO_INITIALIZE_PLACEHOLDERS_WITH_RESTRICTIONS.get(sName);
        if (!sValue.matches(restriction)) {
          StringBuffer error = new StringBuffer();
          error.append('<');
          error.append(getTagName());
          error.append(">: property '");
          error.append(sName);
          error.append("' has an invalid value ");
          error.append(sValue);
          throw new OmFormatException(error.toString());
        }
      }

      // Do specific check if defined
      if (checks.containsKey(sName)) {
        checks.get(sName).check(sValue);
      }
    }
    return sValue;
  }
  @Override
  public void init(QComponent parent, QDocument qd, Element eThis, boolean bImplicit)
      throws OmException {
    Map<String, String> removedAttributes = new HashMap<String, String>();

    // First we need to define and set 'id' before calling super.init
    if (!bImplicit && eThis.hasAttribute(PROPERTY_ID)) {
      String id = eThis.getAttribute(PROPERTY_ID);

      // First we need to define 'id' property
      defineString(PROPERTY_ID, PROPERTYRESTRICTION_ID);

      // As 'id' property doesn't allow placeholders we can set it using superclass method
      super.setString(PROPERTY_ID, id);

      // We remove attribute 'id' before calling setPropertiesFrom method to avoid setting it again
      eThis.removeAttribute(PROPERTY_ID);
      removedAttributes.put(PROPERTY_ID, id);
    }

    // We do this trick to initialize placeholders before calling setPropertiesFrom method
    super.init(parent, qd, eThis, true);

    // Initialize placeholders needed
    if (!bImplicit) {
      for (String property : PROPERTIES_TO_INITIALIZE_PLACEHOLDERS) {
        if (eThis.hasAttribute(property)) {
          String propertyValue = eThis.getAttribute(property);
          if (StandardQuestion.containsPlaceholder(propertyValue)) {
            // We add a placeholder for this property
            placeholders.put(property, propertyValue);

            // We set this property with some value (null for example) to achieve that OM considers
            // that it is set.
            // We need to do this because overriding isPropertySet method from
            // om.stdquestion.QComponent it is not enough to achieve it because checkSetProperty
            // private method from same class does the same check without calling it
            Class<?> type = getPropertyType(property);
            if (type.equals(String.class)) {
              super.setString(property, null);
            } else if (type.equals(Integer.class)) {
              super.setInteger(property, -1);
            } else if (type.equals(Double.class)) {
              super.setDouble(property, 0.0);
            } else if (type.equals(Boolean.class)) {
              super.setBoolean(property, false);
            }

            // We remove attribute before calling setPropertiesFrom method to avoid a format error
            eThis.removeAttribute(property);
            removedAttributes.put(property, propertyValue);
          }
        }
      }
      setPropertiesFrom(eThis);

      // After calling setPropertiesFrom method we need to set again removed attributes
      for (Map.Entry<String, String> removedAttribute : removedAttributes.entrySet()) {
        eThis.setAttribute(removedAttribute.getKey(), removedAttribute.getValue());
      }
    }

    // Specific initializations
    initializeSpecific(eThis);

    // Do now specific checks on properties set without using placeholders
    for (Map.Entry<String, PropertyCheck> check : checks.entrySet()) {
      if (!placeholders.containsKey(check.getKey())) {
        Object value = null;
        Class<?> type = getPropertyType(check.getKey());
        if (type.equals(String.class)) {
          value = super.getString(check.getKey());
        } else if (type.equals(Integer.class)) {
          value = new Integer(super.getInteger(check.getKey()));
        } else if (type.equals(Double.class)) {
          value = new Double(super.getDouble(check.getKey()));
        } else if (type.equals(Boolean.class)) {
          value = new Boolean(super.getBoolean(check.getKey()));
        }
        check.getValue().check(value);
      }
    }
  }