// Validate tagged values cache, filtering on tagged values defined within
  // ShapeChange ...
  public void validateTaggedValuesCache() {
    if (taggedValuesCache == null) {
      // Fetch tagged values collection
      Collection<TaggedValue> tvs = eaClassElement.GetTaggedValues();

      // ensure that there are tagged values
      if (tvs != null) {
        // Allocate cache
        int ntvs = tvs.GetCount();
        taggedValuesCache = options().taggedValueFactory(ntvs);
        // Copy tag-value-pairs, leave out non-ShapeChange stuff and
        // normalize deprecated tags.
        for (TaggedValue tv : tvs) {
          String t = tv.GetName();
          t = document.normalizeTaggedValue(t);
          if (t != null) {
            String v = tv.GetValue();
            if (v.equals("<memo>")) v = tv.GetNotes();
            taggedValuesCache.add(t, v);
          }
        }
      } else {
        taggedValuesCache = options().taggedValueFactory(0);
      }
    }
  } // validateTaggedValuesCache()
  /**
   * Return the documentation attached to the property object. This is fetched from tagged values
   * and - if this is absent - from the 'notes' specific to the EA objects model.
   */
  @Override
  public String documentation() {

    // Retrieve/compute the documentation only once
    // Cache the result for subsequent use
    if (!documentationAccessed) {

      documentationAccessed = true;

      // Try default first
      String s = super.documentation();

      // Try EA notes, if both tagged values fail and ea:notes is the source
      if ((s == null || s.length() == 0)
          && descriptorSource(Options.Descriptor.DOCUMENTATION.toString()).equals("ea:notes")) {
        s = eaClassElement.GetNotes();
        // Fix for EA7.5 bug
        if (s != null) {
          s = EADocument.removeSpuriousEA75EntitiesFromStrings(s);
          super.documentation = options().internalize(s);
        }
      }

      // If result is empty, check if we can get the documentation from a
      // dependency
      if (s == null || s.isEmpty()) {
        for (Iterator<String> i = this.supplierIds().iterator(); i.hasNext(); ) {
          String cid = i.next();
          ClassInfoEA cix = document.fClassById.get(cid);
          if (cix != null) {
            if (cix.name().equalsIgnoreCase(this.name()) && cix.stereotype("featureconcept")) {
              s = cix.documentation();
              break;
            }
          }
        }
      }

      // If result is empty, check if we can get the documentation from a
      // supertype with the same name (added for ELF/INSPIRE)
      if (s == null || s.isEmpty()) {
        HashSet<ClassInfoEA> sts = supertypesAsClassInfoEA();
        if (sts != null) {
          for (ClassInfoEA stci : sts) {
            if (stci.name().equals(this.name())) {
              s = stci.documentation();
              break;
            }
          }
        }
      }

      // Assign what we got or "" ...
      super.documentation = options().internalize(s != null ? s : "");
    }
    return super.documentation;
  } // documentation()
  // Validate constraints cache. This makes sure the constraints cache
  // contains all constraints ordered by their appearance in the class.
  // If constraints are disabled the cache is empty.
  private void validateConstraintsCache() {
    if (constraintsCache == null) {
      // Allocate cache
      constraintsCache = new Vector<Constraint>();
      // Constraints disabled?
      String check = document.options.parameter("checkingConstraints");
      if (check != null && check.equalsIgnoreCase("disabled")) return;

      // Constraints for this class category irrelevant?
      if (!document.options.isClassTypeToCreateConstraintsFor(category())) return;

      // Constraints from selected schemas only?
      if (document.options.isLoadConstraintsForSelectedSchemasOnly()
          && !document.isInSelectedSchemas(this)) {
        return;
      }

      // Filter map for inheritance and overriding by name
      HashMap<String, Constraint> namefilter = new HashMap<String, Constraint>();
      // Access EA constraints data
      Collection<org.sparx.Constraint> constrs = eaClassElement.GetConstraints();
      // Determine constraint types to be parsed as OCL
      String oclTypes = document.options.parameter("oclConstraintTypeRegex");
      // Determine constraint types to be parsed as FOL
      String folTypes = document.options.parameter("folConstraintTypeRegex");
      // Enumerate all constraints found
      // Ensure that there are constraints before continuing
      if (constrs != null) {
        for (org.sparx.Constraint constr : constrs) {
          // Wrap into constraint object
          String type = constr.GetType();
          Constraint oc;
          if (oclTypes.length() > 0 && type.matches(oclTypes)) {
            // 100422/re removed: &&
            // !encodingRule("xsd").equals(Options.ISO19136_2007_INSPIRE)
            OclConstraintEA ocl = new OclConstraintEA(document, this, constr);
            if (ocl.syntaxTree() == null)
              // Text constraint is a fallback in case of parsing
              // issues
              oc = new TextConstraintEA(document, this, constr);
            else oc = ocl;

          } else if (folTypes != null && folTypes.length() > 0 && type.matches(folTypes)) {

            /*
             * only sets up the textual information; parsing is done
             * during model postprocessing - see
             * ModelImpl.postprocessFolConstraints()
             */
            oc = new FolConstraintEA(document, this, constr);

          } else {
            oc = new TextConstraintEA(document, this, constr);
          }
          // Collect in cache
          constraintsCache.add(oc);
          // If the constraint has a name, add it to the filter which
          // blocks inheritance of constraints
          String conam = oc.name();
          if (conam != null && conam.length() > 0) namefilter.put(conam, oc);
        }
      }

      /*
       * Fetch constraints from super-classes. Override by name.
       */

      /*
       * JE: replaced this code with code (see below) that directly
       * accesses the supertype objects, instead of first getting all
       * their IDs and then looking the objects up in the model.
       */

      // HashSet<String> stids = supertypes();
      // if (stids != null) {
      // for (String stid : stids) {
      // ClassInfo stci = model().classById(stid);
      // Vector<Constraint> stcos = null;
      // if (stci != null)
      // stcos = stci.constraints();
      // if (stcos != null) {
      // for (Constraint stco : stcos) {
      // String nam = stco == null ? null : stco.name();
      // if(nam!=null && nam.length()>0 && namefilter.containsKey(nam))
      // continue;
      // constraintsCache.add(stco);
      // }
      // }
      // }
      // }
      HashSet<ClassInfoEA> sts = supertypesAsClassInfoEA();
      if (sts != null) {
        for (ClassInfoEA stci : sts) {
          Vector<Constraint> stcos = null;
          if (stci != null) stcos = stci.constraints();
          if (stcos != null) {
            for (Constraint stco : stcos) {
              String nam = stco == null ? null : stco.name();
              if (nam != null && nam.length() > 0 && namefilter.containsKey(nam)) continue;

              // Is the context of stco still the supertype, or
              // should it not be this (ClassInfoEA)?
              constraintsCache.add(stco);
            }
          }
        }
      }
    }
  }