View Javadoc

    1   /***************************************************************************************
    2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
    3    * http://aspectwerkz.codehaus.org                                                    *
    4    * ---------------------------------------------------------------------------------- *
    5    * The software in this package is published under the terms of the LGPL license      *
    6    * a copy of which has been included with this distribution in the license.txt file.  *
    7    **************************************************************************************/
    8   package org.codehaus.aspectwerkz.definition;
    9
    10  import org.codehaus.aspectwerkz.expression.ExpressionContext;
    11  import org.codehaus.aspectwerkz.expression.ExpressionInfo;
    12  import org.codehaus.aspectwerkz.expression.ExpressionVisitor;
    13  import org.codehaus.aspectwerkz.util.SequencedHashMap;
    14  import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
    15  import org.codehaus.aspectwerkz.cflow.CflowBinding;
    16
    17  import java.util.ArrayList;
    18  import java.util.Collection;
    19  import java.util.HashMap;
    20  import java.util.HashSet;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.Map;
    24  import java.util.Set;
    25
    26  /***
    27   * TODO clean up - remove methods not used, refactor etc.
    28   * <p/>
    29   * Abstraction of the system definition, defines the aspect system.
    30   *
    31   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    32   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
    33   */
    34  public class SystemDefinition {
    35
    36      /***
    37       * Empty hash map.
    38       */
    39      public static final Map EMPTY_HASH_MAP = new HashMap();
    40
    41      /***
    42       * Maps the aspects to it's name.
    43       */
    44      private final Map m_aspectMap = new SequencedHashMap();
    45
    46      /***
    47       * Maps the mixins to it's name.
    48       */
    49      private final Map m_mixinMap = new SequencedHashMap();
    50
    51      /***
    52       * The UUID for this definition.
    53       */
    54      private String m_uuid = "default";
    55
    56      /***
    57       * The include packages.
    58       */
    59      private final Set m_includePackages = new HashSet();
    60
    61      /***
    62       * The exclude packages.
    63       */
    64      private final Set m_excludePackages = new HashSet();
    65
    66      /***
    67       * The prepare packages.
    68       */
    69      private final Set m_preparePackages = new HashSet();
    70
    71      /***
    72       * All prepared pointcuts defined in the system.
    73       */
    74      private final Map m_deploymentScopes = new HashMap();
    75
    76      /***
    77       * Creates a new instance, creates and sets the system cflow aspect.
    78       */
    79      public SystemDefinition(final String uuid) {
    80          setUuid(uuid);
    81          //TODO REMOVE below
    82  //        AspectDefinition systemAspect = new AspectDefinition(
    83  //                CFlowSystemAspect.class.getName(),
    84  //                JavaClassInfo.getClassInfo(CFlowSystemAspect.class),
    85  //                this
    86  //        );
    87  //        systemAspect.setDeploymentModel(CFlowSystemAspect.DEPLOYMENT_MODEL);
    88  //        m_aspectMap.put(CFlowSystemAspect.CLASS_NAME, systemAspect);
    89      }
    90
    91      /***
    92       * Sets the UUID for the definition.
    93       *
    94       * @param uuid the UUID
    95       */
    96      private void setUuid(final String uuid) {
    97          m_uuid = uuid;
    98      }
    99
    100     /***
    101      * Returns the UUID for the definition.
    102      *
    103      * @return the UUID
    104      */
    105     public String getUuid() {
    106         return m_uuid;
    107     }
    108
    109     /***
    110      * Returns the include packages.
    111      *
    112      * @return the include packages
    113      */
    114     public Set getIncludePackages() {
    115         return m_includePackages;
    116     }
    117
    118     /***
    119      * Returns the exclude packages.
    120      *
    121      * @return the exclude packages
    122      */
    123     public Set getExcludePackages() {
    124         return m_excludePackages;
    125     }
    126
    127     /***
    128      * Returns a collection with the aspect definitions registered.
    129      *
    130      * @return the aspect definitions
    131      */
    132     public Collection getAspectDefinitions() {
    133         Collection clone = new ArrayList(m_aspectMap.size());
    134         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
    135             clone.add(it.next());
    136         }
    137         return clone;
    138     }
    139
    140     /***
    141      * Returns a collection with the mixin definitions registered.
    142      *
    143      * @return the mixin definitions
    144      */
    145     public Collection getMixinDefinitions() {
    146         Collection clone = new ArrayList(m_mixinMap.size());
    147         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
    148             clone.add(it.next());
    149         }
    150         return clone;
    151     }
    152
    153     /***
    154      * Returns a collection with the advice definitions registered.
    155      *
    156      * @return the advice definitions
    157      */
    158     public Collection getAdviceDefinitions() {
    159         final Collection adviceDefs = new ArrayList();
    160         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
    161             AspectDefinition aspectDef = (AspectDefinition) it.next();
    162             adviceDefs.addAll(aspectDef.getAroundAdviceDefinitions());
    163             adviceDefs.addAll(aspectDef.getBeforeAdviceDefinitions());
    164             adviceDefs.addAll(aspectDef.getAfterAdviceDefinitions());
    165         }
    166         return adviceDefs;
    167     }
    168
    169     /***
    170      * Returns a specific aspect definition.
    171      *
    172      * @param name the name of the aspect definition
    173      * @return the aspect definition
    174      */
    175     public AspectDefinition getAspectDefinition(final String name) {
    176         return (AspectDefinition) m_aspectMap.get(name);
    177     }
    178
    179     /***
    180      * Returns the mixin definitions matching a specfic expression.
    181      *
    182      * @param ctx the expression context
    183      * @return a list with the mixin definitions
    184      */
    185     public List getMixinDefinitions(final ExpressionContext ctx) {
    186         final List introDefs = new ArrayList();
    187         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
    188             MixinDefinition introDef = (MixinDefinition) it.next();
    189             for (int i = 0; i < introDef.getExpressionInfos().length; i++) {
    190                 if (introDef.getExpressionInfos()[i].getExpression().match(ctx)) {
    191                     introDefs.add(introDef);
    192                 }
    193             }
    194         }
    195         return introDefs;
    196     }
    197
    198     /***
    199      * Returns the interface introductions for a certain class merged with the implementation based introductions as
    200      * well.
    201      *
    202      * @param ctx the expression context
    203      * @return the names
    204      */
    205     public List getInterfaceIntroductionDefinitions(final ExpressionContext ctx) {
    206         if (ctx == null) {
    207             throw new IllegalArgumentException("context can not be null");
    208         }
    209         List interfaceIntroductionDefs = new ArrayList();
    210         for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
    211             AspectDefinition aspectDef = (AspectDefinition) iterator.next();
    212             for (Iterator it = aspectDef.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
    213                 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
    214                 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
    215                 for (int i = 0; i < expressionInfos.length; i++) {
    216                     ExpressionInfo expressionInfo = expressionInfos[i];
    217                     ExpressionVisitor expression = expressionInfo.getExpression();
    218                     if (expression.match(ctx)) {
    219                         interfaceIntroductionDefs.add(introDef);
    220                     }
    221                 }
    222             }
    223         }
    224         return interfaceIntroductionDefs;
    225     }
    226
    227     /***
    228      * Adds a new aspect definition
    229      * For each of its bounded pointcut, register cflow aspects as necessary.
    230      *
    231      * @param aspectDef the aspect definition
    232      */
    233     public void addAspect(final AspectDefinition aspectDef) {
    234         if (aspectDef == null) {
    235             throw new IllegalArgumentException("aspect definition can not be null");
    236         }
    237
    238         synchronized (m_aspectMap) {
    239             if (m_aspectMap.containsKey(aspectDef.getName())) {
    240                 return;
    241             }
    242
    243             // register the "cflow" aspects for this aspect bindings
    244             // note: this one will even support cflow(xx && cflow())
    245             // note: the cflow aspect MUST be registered FIRST for precedence purpose
    246             // so that pcX && cflow(pcX) match on pcX
    247             for (Iterator iterator = aspectDef.getAdviceDefinitions().iterator(); iterator.hasNext();) {
    248                 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator.next();
    249                 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceDefinition.getExpressionInfo());
    250                 for (Iterator cflows = cflowBindings.iterator(); cflows.hasNext();) {
    251                     CflowBinding cflowBinding = (CflowBinding) cflows.next();
    252                     if (!cflowBinding.isCflowBelow()) {
    253                         addAspect(cflowBinding.getAspectDefinition(this, aspectDef.getClassInfo().getClassLoader()));
    254                     }
    255                 }
    256             }
    257
    258             // register the aspect itself
    259             m_aspectMap.put(aspectDef.getName(), aspectDef);
    260
    261             // register the "cflowbelow" aspects for this aspect bindings
    262             // note: this one will even support cflowbelow(xx && cflowbelow())
    263             // note: the cflowbelow aspect MUST be registered LAST for precedence purpose
    264             // so that pcX && cflowbelow(pcX) does not match on just the pcX joinpoint
    265             for (Iterator iterator = aspectDef.getAdviceDefinitions().iterator(); iterator.hasNext();) {
    266                 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator.next();
    267                 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceDefinition.getExpressionInfo());
    268                 for (Iterator cflows = cflowBindings.iterator(); cflows.hasNext();) {
    269                     CflowBinding cflowBinding = (CflowBinding) cflows.next();
    270                     if (cflowBinding.isCflowBelow()) {
    271                         addAspect(cflowBinding.getAspectDefinition(this, aspectDef.getClassInfo().getClassLoader()));
    272                     }
    273                 }
    274             }
    275         }
    276     }
    277
    278     /***
    279      * Adds a new aspect definition, overwrites the previous one with the same name (if there is one).
    280      *
    281      * @param aspectDef the aspect definition
    282      */
    283     public void addAspectOverwriteIfExists(final AspectDefinition aspectDef) {
    284         if (aspectDef == null) {
    285             throw new IllegalArgumentException("aspect definition can not be null");
    286         }
    287         synchronized (m_aspectMap) {
    288             m_aspectMap.put(aspectDef.getName(), aspectDef);
    289         }
    290     }
    291
    292     /***
    293      * Adds a new mixin definition.
    294      *
    295      * @param mixinDef the mixin definition
    296      */
    297     public void addMixinDefinition(final MixinDefinition mixinDef) {
    298         if (mixinDef == null) {
    299             throw new IllegalArgumentException("mixin definition can not be null");
    300         }
    301         synchronized (m_mixinMap) {
    302             if (m_mixinMap.containsKey(mixinDef.getMixinImpl().getName())) {
    303                 MixinDefinition def = (MixinDefinition) m_mixinMap.get(mixinDef.getMixinImpl().getName());
    304                 def.addExpressionInfos(mixinDef.getExpressionInfos());
    305                 return;
    306             }
    307             m_mixinMap.put(mixinDef.getMixinImpl().getName(), mixinDef);
    308         }
    309     }
    310
    311     /***
    312      * Adds a new include package.
    313      *
    314      * @param includePackage the new include package
    315      */
    316     public void addIncludePackage(final String includePackage) {
    317         synchronized (m_includePackages) {
    318             m_includePackages.add(includePackage + '.');
    319         }
    320     }
    321
    322     /***
    323      * Adds a new exclude package.
    324      *
    325      * @param excludePackage the new exclude package
    326      */
    327     public void addExcludePackage(final String excludePackage) {
    328         synchronized (m_excludePackages) {
    329             m_excludePackages.add(excludePackage + '.');
    330         }
    331     }
    332
    333     /***
    334      * Adds a new prepare package.
    335      *
    336      * @param preparePackage the new prepare package
    337      */
    338     public void addPreparePackage(final String preparePackage) {
    339         synchronized (m_preparePackages) {
    340             m_preparePackages.add(preparePackage + '.');
    341         }
    342     }
    343
    344     /***
    345      * Returns the prepare packages.
    346      *
    347      * @return
    348      */
    349     public Set getPreparePackages() {
    350         return m_preparePackages;
    351     }
    352
    353     /***
    354      * Checks if a class should be included.
    355      *
    356      * @param className the name or the class
    357      * @return boolean
    358      */
    359     public boolean inIncludePackage(final String className) {
    360         if (className == null) {
    361             throw new IllegalArgumentException("class name can not be null");
    362         }
    363         if (m_includePackages.isEmpty()) {
    364             return true;
    365         }
    366         for (Iterator it = m_includePackages.iterator(); it.hasNext();) {
    367             String packageName = (String) it.next();
    368             >if (className.startsWith(packageName)) {
    369                 return true;
    370             }
    371         }
    372         return false;
    373     }
    374
    375     /***
    376      * Checks if a class should be excluded.
    377      *
    378      * @param className the name or the class
    379      * @return boolean
    380      */
    381     public boolean inExcludePackage(final String className) {
    382         if (className == null) {
    383             throw new IllegalArgumentException("class name can not be null");
    384         }
    385         for (Iterator it = m_excludePackages.iterator(); it.hasNext();) {
    386             String packageName = (String) it.next();
    387             >if (className.startsWith(packageName)) {
    388                 return true;
    389             }
    390         }
    391         return false;
    392     }
    393
    394     /***
    395      * Checks if a class is in prepare declaration
    396      *
    397      * @param className the name or the class
    398      * @return boolean
    399      */
    400     public boolean inPreparePackage(String className) {
    401         if (className == null) {
    402             throw new IllegalArgumentException("class name can not be null");
    403         }
    404         for (Iterator it = m_preparePackages.iterator(); it.hasNext();) {
    405             String packageName = (String) it.next();
    406             >if (className.startsWith(packageName)) {
    407                 return true;
    408             }
    409         }
    410         return false;
    411     }
    412
    413     /***
    414      * Checks if a context has a pointcut.
    415      *
    416      * @param ctx the expression context
    417      * @return boolean
    418      */
    419     public boolean hasPointcut(final ExpressionContext ctx) {
    420         if (ctx == null) {
    421             throw new IllegalArgumentException("context can not be null");
    422         }
    423         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
    424             AspectDefinition aspectDef = (AspectDefinition) it.next();
    425             for (Iterator it2 = aspectDef.getAdviceDefinitions().iterator(); it2.hasNext();) {
    426                 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
    427                 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
    428                 if (expressionInfo == null) {
    429                     continue;
    430                 }
    431                 ExpressionVisitor expression = expressionInfo.getExpression();
    432
    433                 if (expression.match(ctx)) {
    434                     if (AspectWerkzPreProcessor.DETAILS) {
    435                         System.out.println(
    436                                 "[TRACE - match: " + expression + " @ "
    437                                 + aspectDef.getQualifiedName() + "/" +
    438                                 adviceDef.getName()
    439                         );
    440                         System.out.println("[       for     " + ctx.getReflectionInfo());
    441                         System.out.println("[       within  " + ctx.getWithinReflectionInfo());
    442                         System.out.println("[       type    " + ctx.getPointcutType().toString());
    443                     }
    444                     return true;
    445                 }
    446             }
    447         }
    448         return false;
    449     }
    450
    451     /***
    452      * Checks if a class is advised.
    453      *
    454      * @param ctxs an array with the expression contexts
    455      * @return boolean
    456      */
    457     public boolean isAdvised(final ExpressionContext[] ctxs) {
    458         if (ctxs == null) {
    459             throw new IllegalArgumentException("context array can not be null");
    460         }
    461         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
    462             AspectDefinition aspectDef = (AspectDefinition) it.next();
    463             List advices = aspectDef.getAdviceDefinitions();
    464             for (Iterator it2 = advices.iterator(); it2.hasNext();) {
    465                 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
    466                 for (int i = 0; i < ctxs.length; i++) {
    467                     ExpressionContext ctx = ctxs[i];
    468                     final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
    469                     if (expressionInfo == null) {
    470                         continue;
    471                     }
    472                     if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
    473                         if (AspectWerkzPreProcessor.DETAILS) {
    474                             System.out.println(
    475                                     "[TRACE - earlymatch: " + expressionInfo + " @ "
    476                                     + aspectDef.getQualifiedName() + "/" +
    477                                     adviceDef.getName()
    478                             );
    479                             System.out.println("[       for          " + ctx.getReflectionInfo());
    480                             System.out.println("[       within       " + ctx.getWithinReflectionInfo());
    481                             System.out.println("[       type         " + ctx.getPointcutType().toString());
    482                         }
    483                         return true;
    484                     }
    485                 }
    486             }
    487         }
    488         return false;
    489     }
    490
    491     /***
    492      * Checks if a class is advised.
    493      *
    494      * @param ctx the expression context
    495      * @return boolean
    496      */
    497     public boolean isAdvised(final ExpressionContext ctx) {
    498         if (ctx == null) {
    499             throw new IllegalArgumentException("context can not be null");
    500         }
    501         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
    502             AspectDefinition aspectDef = (AspectDefinition) it.next();
    503             List advices = aspectDef.getAdviceDefinitions();
    504             for (Iterator it2 = advices.iterator(); it2.hasNext();) {
    505                 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
    506                 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
    507                 if (expressionInfo == null) {
    508                     continue;
    509                 }
    510                 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx) /*||
    511                     expressionInfo.getAdvisedCflowClassFilterExpression().match(ctx) ALEX XXX CFLOW*/ ) {
    512                     return true;
    513                 }
    514             }
    515         }
    516         return false;
    517     }
    518
    519     /***
    520      * Checks if a class has an mixin.
    521      *
    522      * @param ctxs an array with the expression contexts
    523      * @return boolean
    524      */
    525     public boolean hasMixin(final ExpressionContext[] ctxs) {
    526         if (ctxs == null) {
    527             throw new IllegalArgumentException("context array can not be null");
    528         }
    529         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
    530             MixinDefinition introDef = (MixinDefinition) it.next();
    531             ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
    532             for (int i = 0; i < expressionInfos.length; i++) {
    533                 ExpressionInfo expressionInfo = expressionInfos[i];
    534                 for (int j = 0; j < ctxs.length; j++) {
    535                     if (expressionInfo.getExpression().match(ctxs[j])) {
    536                         return true;
    537                     }
    538                 }
    539             }
    540         }
    541         return false;
    542     }
    543
    544     /***
    545      * Checks if a class has an mixin.
    546      *
    547      * @param ctx the expression context
    548      * @return boolean
    549      */
    550     public boolean hasMixin(final ExpressionContext ctx) {
    551         if (ctx == null) {
    552             throw new IllegalArgumentException("context can not be null");
    553         }
    554         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
    555             MixinDefinition introDef = (MixinDefinition) it.next();
    556             ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
    557             for (int i = 0; i < expressionInfos.length; i++) {
    558                 ExpressionInfo expressionInfo = expressionInfos[i];
    559                 if (expressionInfo.getExpression().match(ctx)) {
    560                     return true;
    561                 }
    562             }
    563         }
    564         return false;
    565     }
    566
    567     /***
    568      * Checks if a class is advised with an interface introduction.
    569      *
    570      * @param ctxs the expression contexts
    571      * @return boolean
    572      */
    573     public boolean hasIntroducedInterface(final ExpressionContext[] ctxs) {
    574         if (ctxs == null) {
    575             throw new IllegalArgumentException("context array can not be null");
    576         }
    577         for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
    578             AspectDefinition aspectDef = (AspectDefinition) iterator.next();
    579             for (Iterator it = aspectDef.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
    580                 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
    581                 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
    582                 for (int i = 0; i < expressionInfos.length; i++) {
    583                     ExpressionInfo expressionInfo = expressionInfos[i];
    584                     for (int j = 0; j < ctxs.length; j++) {
    585                         if (expressionInfo.getExpression().match(ctxs[i])) {
    586                             return true;
    587                         }
    588                     }
    589                 }
    590             }
    591         }
    592         return false;
    593     }
    594
    595     /***
    596      * Checks if a class is advised with an interface introduction.
    597      *
    598      * @param ctx the expression context
    599      * @return boolean
    600      */
    601     public boolean hasIntroducedInterface(final ExpressionContext ctx) {
    602         if (ctx == null) {
    603             throw new IllegalArgumentException("context can not be null");
    604         }
    605         for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
    606             AspectDefinition aspectDefinition = (AspectDefinition) iterator.next();
    607             for (Iterator it = aspectDefinition.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
    608                 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
    609                 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
    610                 for (int i = 0; i < expressionInfos.length; i++) {
    611                     ExpressionInfo expressionInfo = expressionInfos[i];
    612                     if (expressionInfo.getExpression().match(ctx)) {
    613                         return true;
    614                     }
    615                 }
    616             }
    617         }
    618         return false;
    619     }
    620
    621     /***
    622      * Returns a collection with all deployment scopes in the system.
    623      *
    624      * @return a collection with all deployment scopes  in the system
    625      */
    626     public Collection getDeploymentScopes() {
    627         return m_deploymentScopes.values();
    628     }
    629
    630     /***
    631      * Returns the deployment scope with the name specified.
    632      *
    633      * @param name the name of the deployment scope
    634      * @return the deployment scope with the name specified
    635      */
    636     public DeploymentScope getDeploymentScope(final String name) {
    637         return (DeploymentScope) m_deploymentScopes.get(name);
    638     }
    639
    640     /***
    641      * Adds a deployment scope to the system.
    642      *
    643      * @param deploymentScope the deployment scope
    644      */
    645     public void addDeploymentScope(final DeploymentScope deploymentScope) {
    646         m_deploymentScopes.put(deploymentScope.getName(), deploymentScope);
    647
    648         //TODO do we need to take care of cflow aspects
    649     }
    650
    651     public boolean equals(Object o) {
    652         return ((SystemDefinition) o).m_uuid.equals(m_uuid);
    653     }
    654
    655     public int hashCode() {
    656         return m_uuid.hashCode();
    657     }
    658
    659     /***
    660      * Create a new virtual system definition for the given loader and add the virtual aspect in it.
    661      *
    662      * @param loader
    663      * @return
    664      */
    665     public static SystemDefinition createVirtualDefinitionAt(ClassLoader loader) {
    666         SystemDefinition def = new SystemDefinition(SystemDefinitionContainer.getVirtualDefinitionUuid(loader));
    667         DocumentParser.addVirtualAspect(def);
    668         return def;
    669     }
    670 }