Oversigt over EMF-validering

Sidt opdateret: 23. juni 2005

Dette dokument indeholder en grundlæggende oversigt over EMF's valideringsstruktur og nogle kodeeksempler, der kan lette implementeringen. En mere omfattende beskrivelse af alle funktionerne i EMF kan du finde i EMF: Eclipse Modeling Framework, Second Edition (Addison-Wesley Professional, 2008) eller i Javadoc vedrørende selve strukturens klasser.

Introduktion

Vil du være sikker på, at din models data overholder de begrænsninger, der er fastlagt for dataene? Denne oversigt giver med tre hurtige trin en ide om, hvordan du kan opnå det ved hjælp af EMF's valideringsstruktur.


Definitioner

Invariant - implementeres som klassemetode, defineret på modellen. Betragtes som et vægtigere udsagn om validitet end en betingelse. Eksempel: hasUSState()

Rose-model - Invariant
Figur 1. Rose-model - Invariant

Navngivet betingelse - implementeres som metode på en ekstern validatorklasse, ikke på selve modellen. Betragtes som et mindre vægtigt udsagn om validitet end en Invariant. Eksempel: NonNegativeQuantity, ValidShipDate

Rose-model - betingelse
Figur 2. Rose-model - betingelse

Skemabaseret betingelse - som navngivet betingelse, men defineret i et skema. Eksempel: 'quantity skal være en int mellem 0 og 100'. Eftersom disse typer betingelser har kendte funktionsmåder, kræves ikke yderligere arbejde for at implementere dem. Alle betingelser med en simpel type implementeres automatisk af kodegeneratoren.

  <xsd:element name="quantity">
    <xsd:simpleType>
      <xsd:restriction base="xsd:int">
        <xsd:minInclusive value="0"/>
        <xsd:maxInclusive value="100"/>
      </xsd:restriction>
    </xsd:simpleType>
  </xsd:element>

Indhold 

Trin 1: Opret betingelser i modellen
Trin 2: Definér betingelser
Trin 3: Generér og udfør
Appendiks: Udvidede emner

 indhold

Trin 1: Opret betingelser i modellen

Som for al EMF-udvikling kan du starte fra annoteret Java, et XML-skema eller en Rose-model. Metoden til definition af betingelser afhænger af kilden til modellen.

For Rose repræsenterer stereotypen <<inv>> for en funktion en Invariant. Du finder et eksempel på det i Figur 1 ovenfor.

Hvis du vil tilføje en betingelse i Rose 98, skal du åbne modellen og derefter vælge en klasse (f.eks. "Item" i det eksempel, der vises her). Højreklik, og vælg "Open Specification" (eller dobbeltklik på "Item"). Vælg derefter skillebladet "Ecore", og klik på feltet constraints for at tilføje betingelser som i Figur 2 ovenfor.

For et skema kan du definere en<xsd:restriction/>-betingelse (som ovenfor) eller en navngivet betingelse:

  <xsd:complexType name="Item">
    <xsd:annotation>
       <xsd:appinfo source="http://www.eclipse.org/emf/2002/Ecore" 
       ecore:key="constraints">
         NonNegativeQuantity ValidShipDate
       </xsd:appinfo>
    </xsd:annotation>
  ...
  </xsd:complexType>

For annoteret Java kan du definere Invariant-forekomster og betingelser på denne måde:

Invariant

  /**
    * @model
    */
    public interface USAddress
    {
      ...
      /**
       * @model
       */
      boolean hasUSState(
        DiagnosticChain diagnostics, 
        Map context);
      ...
    }

Betingelse

  /**
   * @model 
  annotation="http://www.eclipse.org/emf/2002/Ecore 
  constraints='NonNegativeQuantity ValidShipDate'"
   */
    public interface Item
    {
       ...
    }

 indhold

Trin 2: Definér betingelser

Importér din model i EMF, og generér derefter kode som i trin 2-3 i øveprogrammet Generér en EMF-model.

Den genererede kode vil se omtrent sådan ud:

Invariant, com.example.ppo.impl.USAddressImpl

  public boolean hasUSState(
    DiagnosticChain diagnostics, 
    Map context)
  {
    // TODO: implement this method
    // -> specify the condition that violates 
    //    the invariant
    // -> verify the details of the diagnostic, 
    //    including severity and message
    // Ensure that you remove @generated or 
    // mark it @generated NOT
    if (false)
    {
      if (diagnostics != null)
      {
        diagnostics.add(
          new BasicDiagnostic(
             Diagnostic.ERROR,
             PPOValidator.DIAGNOSTIC_SOURCE,
             PPOValidator.US_ADDRESS__HAS_US_STATE,
             EcorePlugin.INSTANCE.getString(
               "_UI_GenericInvariant_diagnostic", 
               new Object[] {
                 "hasUSState",
                 EObjectValidator.getObjectLabel(this, 
                   context)}),
             new Object [] { this }));
      }
      return false;
    }
    return true;
  }

Betingelse, com.example.ppo.util.PPOValidator

  public boolean validateItem_ValidShipDate(
    Item item, DiagnosticChain diagnostics, 
    Map context)
  {
    // TODO implement the constraint
    // -> specify the condition that violates 
    //    the constraint
    // -> verify the diagnostic details, 
    //    including severity, code, and message
    // Ensure that you remove @generated or 
    // mark it @generated NOT
    if (false)
    {
      if (diagnostics != null)
      {
        diagnostics.add(
          new BasicDiagnostic(
            Diagnostic.ERROR,
            DIAGNOSTIC_SOURCE,
            0,
            EcorePlugin.INSTANCE.getString(
              "_UI_GenericConstraint_diagnostic",
              new Object[] {
                "ValidShipDate", 
                getObjectLabel(item, context) }),
            new Object[] { item }));
      }
      return false;
    }
    return true;
  }

Der genereres også kode i validatoren, f.eks. i com.example.ppo.util.PPOValidator, for hver defineret Invariant, men disse metoder delegerer blot til Invariant-metoderne på selve objekterne, f.eks.:

  public boolean validateUSAddress_hasUSState(USAddress usAddress, 
    DiagnosticChain diagnostics, Map context)
  {
    return usAddress.hasUSState(diagnostics, context);
  }

I begge tilfælde skal den genererede kode ændres manuelt for at beskrive over for EMF, hvordan Invariant'en eller betingelsen skal implementeres. I det første tilfælde skal du ændre første linje fra:

    if (false)

til

    if ("US".equals(getCountry()) && getState() == null).


 indhold

Trin 3: Generér og udfør

Når du er færdig med at definere betingelser, skal du starte et nyt arbejdsområde. Der er flere oplysninger i trin 4 i øveprogrammet Generér en EMF-model.

Din model har nu punktet Validér på editormenuen.

Start validering fra brugergrænsefladen
Figur 3. Start validering fra brugergrænsefladen

Hvis modellen indeholder ugyldige data, opstår problemer under valideringen, og der vises en dialogboks som den i figur 4.

Dialogboksen Valideringsproblemer
Figur 4. Dialogboksen Valideringsproblemer

Hvis du vælger en af fejlfindingsmeddelelserne i dialogboksen, før du klikker på knappen OK, vælges det objekt i editoren, som er årsag til meddelelsen. Der vises også markeringer af disse fejlfindingsmeddelelser i oversigten Problemer i Eclipse.


 indhold

Udvidede emner

Du kan implementere validering på en anden måde, f.eks. når en fil skal gemmes eller åbnes, ved at gøre noget i stil med dette:

  public static boolean validateObject(EObject eObject)
  {
    Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObject);
    return diagnostic.getSeverity() == Diagnostic.OK;
  }

Når der bruges en Diagnostician, beregnes resultatet af evalueringen på grundlag af en Diagnostic-forekomst, ikke blot en returneret boolesk værdi. Det sætter dig i stand til at afgøre, hvilket niveau der udgør en fejl, og vise oplysninger om de betingelser og Invariant-forekomster, der ikke er overholdt. Hvis du f.eks. kun er interesseret i fejl og advarsler, kan du opnå det sådan her:

  public static boolean validateObject(EObject eObject)
  {
    Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObject);
    if (diagnostic.getSeverity() == Diagnostic.ERROR || 
      diagnostic.getSeverity() == Diagnostic.WARNING)
    {
      System.err.println(diagnostic.getMessage());
      for (Iterator i=diagnostic.getChildren().iterator(); i.hasNext();)
      {
        Diagnostic childDiagnostic = (Diagnostic)i.next();
        switch (childDiagnostic.getSeverity())
        {
          case Diagnostic.ERROR:
          case Diagnostic.WARNING:
            System.err.println("\t" + childDiagnostic.getMessage());
        }
      }
      return false;
    }
    return true;
  }

Bemærk, at når Diagnostician.INSTANCE.validate() bruges, får du foræret noget indbygget Ecore-betingelsesvalidering takket være EObjectValidator, basis for alle genererede pakkevalidatorklasser. Det drejer sig om: