View Javadoc

1   /*
2    * Copyright (c) 2001-2005,
3    * RedVerst Group, ISP RAS http://www.ispras.ru
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are met:
8    *
9    * 1. Redistributions of source code must retain the above copyright notice, this
10   *    list of conditions and the following disclaimer.
11   *
12   * 2. Redistributions in binary form must reproduce the above copyright notice,
13   *    this list of conditions and the following disclaimer in the documentation
14   *    and/or other materials provided with the distribution.
15   *
16   * 3. The names "ATP", "TreeDL", "RedVerst", "ISP RAS"
17   *    may not be used to endorse or promote products derived from this software
18   *    without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  
32  package com.unitesk.atp.treedl;
33  
34  import java.io.File;
35  import java.util.HashMap;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  
40  import com.unitesk.atp.tool.PluginClass;
41  import com.unitesk.atp.tree.Node;
42  import com.unitesk.atp.messages.*;
43  import com.unitesk.atp.treedl.TDL.*;
44  
45  public class Checker implements TDL_Visitor
46  {
47      /***
48       * Returns checker version.
49       */
50      public static String getVersion()
51      {
52          return PluginClass.getRevision( "$Revision: 1.69 $" ) + "-beta";
53      }
54      
55      /***
56       * Returns localized entity name.
57       * 
58       * @param cls  Class with that provides localized entity name.
59       *             Often this class implements entity. 
60       * @param name Name of resource containing localized entity name.
61       * @return     Localized entity name
62       */
63      private String getString( Class cls, String name )
64      {
65          return mbox.getStringManager().getString( cls, name );
66      }
67  
68      /***
69       * Register loaded module in map.
70       * 
71       * @param module   Module to be registered.
72       */
73      private void registerModule( TDL_Module module )
74      {
75          QID name = module.getRoot().getName();
76          TDL_Module oldModule = moduleMap.getModule( name ); 
77          if( oldModule != null )
78          {
79              throw new InternalError( "Impossible to register module with the same name twice" );
80          }
81          moduleMap.putModule( module );
82          module.getRoot().setModuleMap( moduleMap );
83      }
84      
85      /***
86       * Check semantics of module. Module will be registered in map. 
87       * 
88       * @param moduleMap    Map of loaded modules.
89       * @param module       Module to be chacked.
90       */
91      public static void check( TDL_ModuleMap moduleMap, TDL_Module module )
92      {
93          Module moduleRoot = module.getRoot();
94          switch( moduleRoot.getState().key() )
95          {
96          case CheckState.Key.NOT_CHECKED:
97              moduleRoot.setState( CheckState.CHECKING );
98              break;
99          case CheckState.Key.CHECKING:
100             module.getMessageBox().fatal( new Message.ModuleCircularDependence
101                                               ( module.getFile()
102                                               , moduleRoot.getName().getValue()
103                                               )
104                                         );
105             return;
106         case CheckState.Key.CHECKED:
107             return;
108         default:
109             throw new IllegalArgumentException( "checkStatus = " + moduleRoot.getState() );
110         }
111 
112         // visit
113 
114         Checker checker = new Checker( moduleMap, module );
115         
116         module.getMessageBox().info( new FileStatusMessage
117                                          ( CheckFileStatus.status
118                                          , module.getFile().toString()
119                                          )
120                                    );
121         checker.registerModule( module );
122         
123         moduleRoot.accept( checker );
124 
125         moduleRoot.setState( CheckState.CHECKED );
126     }
127    
128     private TDL_ModuleMap moduleMap;
129     private TDL_Module module;
130     private MessageBox mbox;
131     private File file;
132 
133     public MessageBox getMessageBox() { return mbox; }
134 
135     public File getFile() { return file; }
136     
137     protected Checker( TDL_ModuleMap moduleMap, TDL_Module module )
138     {
139         this.moduleMap = moduleMap;
140         this.module = module;
141         mbox = module.getMessageBox();
142         file = module.getFile();
143     }
144 
145     private void visit( Node node )        
146     {
147         if( node != null ) node.accept( this );
148     }
149     
150     private void visit( List/*Acceptor*/ list )
151     {
152         for( int i = 0; i < list.size(); i++ )
153         {
154             ((Node)(list.get( i ))).accept( this );
155         }
156     }
157     
158     private void registerType( ModuleTypeMember member )
159     {
160         ID name = member.getName();
161         String nameStr = name.getValue();
162         NodeTypeDecl rootNodeType = TDL_Module.getRootNodeType();
163         if(    member != rootNodeType 
164             && name.equals( rootNodeType.getName() ) 
165           )
166         {
167             mbox.error( new Message.ReservedNameNode
168                             ( file
169                             , name.getPosition()
170                             )
171                       ); 
172             return;
173         }
174         Map/*String,ModuleMember*/ memberMap = module.getRoot().getMemberMap();
175         if( memberMap.containsKey( nameStr ) )
176         {
177             mbox.error( new Message.ModuleMemberAlreadyDefined
178                             ( file
179                             , name.getPosition()
180                             , nameStr
181                             , ((ModuleMember)memberMap.get( nameStr )).getName().getPosition()
182                             )
183                       ); 
184             return;
185         }
186         memberMap.put( nameStr, member );
187     }
188 
189     private void registerOperation( OperationDecl operationDecl )
190     {
191         ID name = operationDecl.getName();
192         for( int i = 0; i < operationDecl.sizeOptParameterDeclList(); i++ )
193         {
194             ParameterDecl parDecl = operationDecl.getOptParameterDeclList(i);
195             if( parDecl instanceof VirtualParameterDecl )
196             {
197                 visit( ((VirtualParameterDecl)parDecl).getType() );
198             } else {
199                 visit( ((NonVirtualParameterDecl)parDecl).getType() );
200             }
201         }
202         StringBuffer sb = new StringBuffer();
203         sb.append( "(" );
204         for( int i = 0; i < operationDecl.sizeOptParameterDeclList(); i++ )
205         {
206             if( i > 0 ) sb.append( "," );
207             ParameterDecl parDecl = operationDecl.getOptParameterDeclList(i);
208             if( parDecl instanceof VirtualParameterDecl )
209             {
210                 sb.append( TDL.toString( ((VirtualParameterDecl)parDecl).getType() ) );
211             } else {
212                 sb.append( TDL.toString( ((NonVirtualParameterDecl)parDecl).getType() ) );
213             }
214         }
215         sb.append( ")" );
216         String nameStr = sb.toString();
217         operationDecl.setParameters( nameStr );
218         nameStr = name.getValue() + nameStr;
219 
220         Map/*String,ModuleMember*/ memberMap = module.getRoot().getMemberMap();
221         if( memberMap.containsKey( nameStr ) )
222         {
223             mbox.error( new Message.ModuleMemberAlreadyDefined
224                             ( file
225                             , name.getPosition()
226                             , nameStr
227                             , ((ModuleMember)memberMap.get( nameStr )).getName().getPosition()
228                             )
229                       ); 
230             return;
231         }
232         memberMap.put( nameStr, operationDecl );
233     }
234 
235     private Map/*ID,TDL_Module*/ synonymToBaseModuleMap 
236         = new HashMap/*ID,TDL_Module*/();
237     
238     /***
239      * Register synonym of module.
240      * @param synonym
241      * @param module
242      */
243     private void registerBaseModule( ID synonym, TDL_Module module )
244     {
245         if( synonymToBaseModuleMap.containsKey( synonym ) )
246         {
247             mbox.error( new Message.SynonymAlreadyDefined
248                             ( file
249                             , synonym.getPosition()
250                             , synonym.getValue()
251                             ,  ((TDL_Module)synonymToBaseModuleMap.get( synonym ))
252                               .getRoot().getName().getFirst().getPosition()
253                             )
254                       );
255             return;
256         }
257         synonymToBaseModuleMap.put( synonym, module );
258     }
259     
260     /***
261      * Find module by synonym.
262      * 
263      * @param synonym  Synonym for module.
264      * @return         Module for given synonym or null if this synonym is not defined.  
265      */
266     private TDL_Module findBaseModule( ID synonym )
267     {
268         return (TDL_Module)synonymToBaseModuleMap.get( synonym ); 
269     }
270 
271     public void visitModule( Module node )
272     {
273         // register synonym of this module
274         registerBaseModule( node.getName().getLast(), module );
275         NodeTypeDecl rootNodeType = TDL_Module.getRootNodeType();
276         // register 'Node' as module member
277         registerType( rootNodeType );
278 
279         // check base modules
280         int ec = mbox.getErrorCount();
281         visit( node.getOptBaseModuleList() );
282         if( mbox.getErrorCount() > ec ) return;
283         addReferencedModule( node );
284 
285         // register module types
286         for( int i = 0; i < node.sizeOptMemberList(); i++ )
287         {
288             ModuleMember member = node.getOptMemberList( i );
289             if( member instanceof ModuleTypeMember )
290             {
291                 registerType( (ModuleTypeMember)member );
292                 if(    member instanceof NodeTypeDecl 
293                     && ((NodeTypeDecl)member).isRoot() 
294                   )
295                 {
296                     NodeTypeDecl nodeType = (NodeTypeDecl)member;
297                     if( node.getOptRootNodeType() != null )
298                     {
299                         mbox.error( new Message.RootNodeTypeAlreadyDefined
300                                         ( file
301                                         , nodeType.getName().getPosition()
302                                         , node.getOptRootNodeType().getName().getPosition()
303                                         )
304                                   );
305                     } else {
306                         node.setOptRootNodeType( nodeType );
307                     }
308                 }
309             }
310         }
311         // register module operations
312         for( int i = 0; i < node.sizeOptMemberList(); i++ )
313         {
314             ModuleMember member = node.getOptMemberList( i );
315             if( member instanceof OperationDecl )
316             {
317                 registerOperation( (OperationDecl)member );
318             }
319         }
320         // check module members
321         visit( node.getOptMemberList() );
322     }
323     
324     private Map/*String,BaseModule*/ 
325         fullNameToBaseModuleMap = new HashMap/*String,BaseModule*/();
326 
327     public void visitBaseModule( BaseModule node )
328     {
329         QID name = node.getName();
330         String nameStr = name.getValue();
331         TDL_Module baseModule = moduleMap.loadModule( name );
332         if( baseModule == null || !name.equals( baseModule.getRoot().getName() ) ) 
333         {
334             mbox.error( new Message.BaseModuleNotFound
335                             ( file
336                             , name.getFirst().getPosition()
337                             , name.getValue()
338                             )
339                       );
340             return;
341         }
342         node.setModule( baseModule );
343         
344         if( fullNameToBaseModuleMap.containsKey( nameStr ) )
345         {
346             mbox.error( new Message.BaseModuleAlreadyReferenced
347                             ( file
348                             , name.getFirst().getPosition()
349                             , nameStr
350                             ,  ((BaseModule)fullNameToBaseModuleMap.get( nameStr ))
351                               .getName().getFirst().getPosition()
352                             )
353                       );
354             return;
355         }
356         fullNameToBaseModuleMap.put( nameStr, node );
357         
358         ID synonym = node.getOptSynonym();
359         if( synonym == null )
360         {
361             synonym = name.getLast();
362         }
363 
364         registerBaseModule( synonym, baseModule );
365         
366         String baseLanguage 
367             = baseModule.getRoot().getProperty( Module.PROPERTY_TREEDL_LANGUAGE
368                                               , Module.DEFAULT_TREEDL_LANGUAGE
369                                               );
370         String language 
371             = module.getRoot().getProperty( Module.PROPERTY_TREEDL_LANGUAGE
372                                           , Module.DEFAULT_TREEDL_LANGUAGE
373                                           );
374         if(    !language.equals( baseLanguage )
375             && !baseLanguage.equals( Module.DEFAULT_TREEDL_LANGUAGE )
376           )
377         {
378             mbox.error( new Message.IncompatibleBaseModuleLanguage
379                             ( file
380                             , ((Module)node.getParent()).getName().getFirst().getPosition()
381                             , language
382                             , baseLanguage
383                             , node.getName().getValue()
384                             )
385                       );
386         }
387         
388         check( moduleMap, baseModule );
389         List/*Module*/ ml = baseModule.getRoot().getReferencedModuleList();
390         for( int i = 0; i < ml.size(); i++ )
391         {
392             addReferencedModule( (Module)ml.get(i) );
393         }
394     }
395     
396     private void addReferencedModule( Module m )
397     {
398         if( findReferencedModule( m.getName().getValue() ) >= 0 ) return;
399         module.getRoot().getReferencedModuleList().add( m );
400          module
401         .getRoot()
402         .getReferencedModuleMap()
403         .put( m.getName().getValue()
404             , new Integer( module.getRoot().sizeReferencedModuleList() - 1 )
405             );
406     }
407     
408     private int findReferencedModule( String name )
409     {
410         Integer index = (Integer)module.getRoot().getReferencedModuleMap().get( name );
411         if( index == null ) return -1;
412         return index.intValue();        
413     }
414     
415     public void visitModuleMember( ModuleMember node )
416     {
417         switch( node.getState().key() )
418         {
419         case CheckState.Key.NOT_CHECKED:
420             node.setState( CheckState.CHECKING );
421             break;
422         case CheckState.Key.CHECKING:
423             mbox.error( new Message.TypeCircularDependence
424                             ( module.getFile()
425                             , node.getName().getPosition()
426                             , getString( TDL.class
427                                        ,   node instanceof NodeTypeDecl 
428                                          ?   "message.node_type"
429                                            :   ((ConstTypeDecl)node).isFlags()
430                                              ?   "message.flags_type"
431                                                : "message.enum_type"  
432                                        )
433                             , node.getName().getValue()
434                             )
435                       );
436             node.setState( CheckState.CHECKED );
437             break;
438         case CheckState.Key.CHECKED:
439             break;
440         }
441     }
442 
443     public void visitModuleTypeMember( ModuleTypeMember node )
444     {
445         visitModuleMember( node );
446         if( node.getState() == CheckState.CHECKED ) return;
447 
448         node.getOptInheritorList().add( node );
449 
450         TypeRef baseTypeRef = node.getOptBaseType();
451         if( baseTypeRef != null )
452         {
453             // check base type
454             visit( baseTypeRef );
455             ModuleTypeMember baseType = baseTypeRef.getType();
456             if( baseType != null )
457             {
458                 baseType.getOptInheritorList().add( node );
459             }
460         }
461     }
462 
463     public void visitConstTypeDecl( ConstTypeDecl node )
464     {
465         visitModuleTypeMember( node );
466         if( node.getState() == CheckState.CHECKED ) return;
467 
468         TypeRef baseTypeRef = node.getOptBaseType();
469         if( baseTypeRef != null )
470         {
471             ModuleTypeMember baseType = baseTypeRef.getType();
472             if( baseType != null )
473             {
474                 if( baseType instanceof ConstTypeDecl )
475                 {
476                     ConstTypeDecl baseConstType = (ConstTypeDecl)baseType; 
477                     if( node.isFlags() == ((ConstTypeDecl)baseType).isFlags() )
478                     {
479                         visit( baseConstType );
480                         node.getFullConstantList().addAll( baseConstType.getFullConstantList() );
481                         node.getConstToTypeMap().putAll( baseConstType.getConstToTypeMap() );
482                     } else {
483                         mbox.error( new Message.IncompatibleBaseType
484                                         ( file
485                                         , node.getName().getPosition()
486                                         , getString( TDL.class
487                                                    ,   node.isFlags() 
488                                                      ?   "message.flags_type" 
489                                                        : "message.enum_type" 
490                                                    )
491                                         , node.getName().getValue()
492                                         )
493                                   );
494                     }
495                 } else {
496                     mbox.error( new Message.IncompatibleBaseType
497                                     ( file
498                                     , node.getName().getPosition()
499                                     , getString( TDL.class
500                                                ,   node.isFlags() 
501                                                  ?   "message.flags_type" 
502                                                    : "message.enum_type" 
503                                                )
504                                     , node.getName().getValue()
505                                     )
506                               );
507                 }
508             }
509         }
510         for( int i = 0; i < node.sizeOptConstantList(); i++ )
511         {
512             ID constant = node.getOptConstantList( i );
513             if( node.getConstToTypeMap().containsKey( constant ) )
514             {
515                 mbox.error( new Message.ConstantAlreadyDefined
516                                 ( file
517                                 , constant.getPosition()
518                                 , constant.getValue()
519                                 )
520                           );
521             } else {
522                 node.getConstToTypeMap().put( constant, node );
523             }
524         }
525         node.getFullConstantList().addAll( node.getOptConstantList() );
526         node.setState( CheckState.CHECKED );
527     }
528     
529     public void visitNodeTypeDecl( NodeTypeDecl node )
530     {
531         visitModuleTypeMember( node );
532         if( node.getState() == CheckState.CHECKED ) return;
533 
534         Module module = (Module)node.getParent(); 
535         if( !module.isTree() )
536         {
537             mbox.error( new Message.NodeTypeCantBeInModule
538                             ( file
539                             , node.getName().getPosition()
540                             )
541                       );
542         }
543 
544         TypeRef baseTypeRef = node.getOptBaseType();
545         NodeTypeDecl baseNodeType = null; 
546         if( baseTypeRef != null )
547         {
548             ModuleTypeMember baseType = baseTypeRef.getType();
549             if( baseType != null )
550             {
551                 if( baseType instanceof NodeTypeDecl )
552                 {
553                     baseNodeType = (NodeTypeDecl)baseType;
554                 } else {
555                     mbox.error( new Message.IncompatibleBaseType
556                                     ( file
557                                     , node.getName().getPosition()
558                                     , getString( TDL.class, "message.node_type" )
559                                     , node.getName().getValue()
560                                     )
561                               );
562                 }
563             }
564         }
565         if( baseNodeType == null )
566         {
567             baseNodeType = TDL_Module.getRootNodeType();
568         }
569         visit( baseNodeType );
570         node.setBaseNodeType( baseNodeType );
571         if( node.isAbstract() && !baseNodeType.isAbstract() )
572         {
573             mbox.error( new Message.AbstractBaseNodeRequired
574                             ( file
575                             , node.getName().getPosition()
576                             , node.getName().getValue()
577                             )
578                       );
579         }
580         Map/*ID,Field*/ nameToFieldMap = node.getNameToFieldMap();
581         nameToFieldMap.putAll( baseNodeType.getNameToFieldMap() );
582         node.getFullFieldList().addAll( baseNodeType.getFullFieldList() );
583         visit( node.getOptMemberList() );
584         if( !node.isAbstract() )
585         {
586             for( Iterator i = nameToFieldMap.values().iterator();
587                  i.hasNext(); 
588                )
589             {
590                 Field field = (Field)i.next();
591                 if(    field.getParent() != node 
592                     && field.checkModifiers( Modifiers.ABSTRACT ) 
593                   )
594                 {
595                     mbox.error( new Message.AbstractAttributeShouldBeOverridden
596                                     ( file
597                                     , node.getName().getPosition()
598                                     , node.getName().getValue()
599                                     )
600                               );
601                 }
602             }
603         }
604         node.setState( CheckState.CHECKED );
605     }
606     
607     public void visitConstructorCodeMember( ConstructorCodeMember node )
608     {
609     }
610     
611     public void visitBodyCodeMember( BodyCodeMember node )
612     {
613     }
614 
615     public void visitField( Field node )
616     {
617         NodeTypeDecl nodeType = (NodeTypeDecl)node.getParent();
618         ID name = node.getName();
619         
620         if( nodeType.getNameToFieldMap().containsKey( name ) )
621         {
622             Field baseField = (Field)nodeType.getNameToFieldMap().get( name );
623             if( baseField.getParent() == nodeType )
624             {
625                 mbox.error( new Message.NodeMemberAlreadyDefined
626                                 ( file
627                                 , name.getPosition()
628                                 , name.getValue()
629                                 )
630                           );
631                 return;
632             }
633             node.setBaseField( baseField );
634         }
635         nodeType.getNameToFieldMap().put( name, node );
636         nodeType.getFullFieldList().add( node );
637         
638         visit( node.getType() );
639         
640         if( node.getType() instanceof TDL.NodeType )
641         {
642             // if type not found
643             if( ((TDL.NodeType)node.getType()).getType().getType() == null ) return;
644             
645             // add this type to list of parent types for type of member
646             TDL_Module.insert(  ((TDL.NodeType)node.getType())
647                                .getType()
648                                .getType()
649                                .getOptParentTypeList()
650                              , nodeType 
651                              ); 
652         } 
653         
654         if( node.isChild() )
655         {
656             if(    node.checkModifiers( Modifiers.ABSTRACT ) 
657                 || node.checkModifiers( Modifiers.CUSTOM ) 
658                 || node.checkModifiers( Modifiers.NOSET )
659               )
660             {
661                 mbox.error( new Message.ChildCantBeAbstractCustomNoSet
662                                 ( file 
663                                 , node.getName().getPosition()
664                                 , node.getName().getValue()
665                                 )
666                           );
667             }
668         }
669 
670         Field baseField = node.getBaseField();
671         if( baseField != null )
672         {
673             // overriding field
674             if( node.isChild() != baseField.isChild() )
675             {
676                 mbox.error( new Message.IncompatibleBaseField
677                                 ( file 
678                                 , node.getName().getPosition()
679                                 , getString( TDL.class
680                                            ,   node.isChild() 
681                                              ?   "message.child" 
682                                                : "message.attribute" 
683                                            )
684                                 , node.getName().getValue()
685                                 )
686                           );
687             }
688             
689             if( !node.getType().equals( baseField.getType() ) )
690             {
691                 mbox.error( new Message.IncompatibleBaseFieldType
692                                 ( file 
693                                 , node.getName().getPosition()
694                                 , getString( TDL.class
695                                            ,   node.isChild() 
696                                              ?   "message.child" 
697                                                : "message.attribute" 
698                                            )
699                                 , node.getName().getValue()
700                                 )
701                           );
702             }
703 
704             if( !baseField.checkModifiers( Modifiers.ABSTRACT ) )
705             {
706                 if( !node.checkModifiers( Modifiers.OVERRIDE ) )
707                 {
708                     mbox.error( new Message.OverrideModifierRequired
709                                     ( file 
710                                     , node.getName().getPosition()
711                                     , getString( TDL.class
712                                                ,   node.isChild() 
713                                                  ?   "message.child" 
714                                                    : "message.attribute" 
715                                                )
716                                     , node.getName().getValue()
717                                     )
718                               );
719                 }
720 
721                 if( node.checkModifiers( Modifiers.ABSTRACT ) )
722                 {
723                     mbox.error( new Message.AbstractAttributeCantOverrideNonAbstract
724                                     ( file 
725                                     , node.getName().getPosition()
726                                     , node.getName().getValue()
727                                     )
728                               );
729                 }
730 
731                 if( node.checkModifiers( Modifiers.CUSTOM ) )
732                 {
733                     mbox.error( new Message.CustomModifierNotAllowedWhenOverridingNonAbstract
734                                     ( file 
735                                     , node.getName().getPosition()
736                                     , node.getName().getValue()
737                                     )
738                               );
739                 }
740             }
741             
742             if(    node.checkModifiers( Modifiers.LATE )
743                 && !baseField.checkModifiers( Modifiers.LATE )
744               )
745             {
746                 mbox.error( new Message.NonLateAttributeCantOverrideLate
747                                 ( file 
748                                 , node.getName().getPosition()
749                                 , node.getName().getValue()
750                                 )
751                           );
752             }
753             if(    node.checkModifiers( Modifiers.NOSET )
754                 && !baseField.checkModifiers( Modifiers.NOSET )
755               )
756             {
757                 mbox.error( new Message.NoSetAttributeCanOverrideOnlyNoSet
758                                 ( file 
759                                 , node.getName().getPosition()
760                                 , node.getName().getValue()
761                                 )
762                           );
763             }
764             if(    node.checkModifiers( Modifiers.SETONCE )
765                 != baseField.checkModifiers( Modifiers.SETONCE )
766               )
767             {
768                 mbox.error( new Message.IncompatibleSetOnceModifiers
769                                 ( file 
770                                 , node.getName().getPosition()
771                                 , node.getName().getValue()
772                                 )
773                           );
774             }
775         }
776 
777         if( node.checkModifiers( Modifiers.ABSTRACT ) ) 
778         {
779             if( !nodeType.isAbstract() )
780             {
781                 mbox.error( new Message.AbstractAttributeShouldBeInAbstractNode
782                                 ( file 
783                                 , node.getName().getPosition()
784                                 , node.getName().getValue()
785                                 )
786                           );
787                 return;
788             }
789             if( node.getOptGetCode() != null || node.getOptSetCode() != null )
790             {
791                 mbox.error( new Message.CustomCodeNotAllowedForAbstractAttribute
792                                 ( file 
793                                 , node.getName().getPosition()
794                                 , node.getName().getValue()
795                                 )
796                           );
797             }
798         }
799         if(    node.checkModifiers( Modifiers.CUSTOM ) 
800             && node.checkModifiers( Modifiers.ABSTRACT ) 
801           )
802         {
803             mbox.error( new Message.AbstractAttributeCantBeCustom
804                             ( file 
805                             , node.getName().getPosition()
806                             , node.getName().getValue()
807                             )
808                       );
809         }
810         if(    !TDL_Module.hasGet( node )
811             && !nodeType.isAbstract()
812           )
813         {
814             mbox.error( new Message.GetOperationNotDefined
815                             ( file 
816                             , node.getName().getPosition()
817                             , node.getName().getValue()
818                             )
819                       );
820         }
821         if(    !node.checkModifiers( Modifiers.NOSET )
822             && !TDL_Module.hasSet( node )
823             && !nodeType.isAbstract()
824           )
825         {
826             mbox.error( new Message.SetOperationNotDefined
827                             ( file 
828                             , node.getName().getPosition()
829                             , node.getName().getValue()
830                             )
831                       );
832         }
833         if( node.checkModifiers( Modifiers.NOSET ) ) 
834         {
835             if( !node.checkModifiers( Modifiers.LATE ) )
836             {
837                 mbox.error( new Message.NoSetAttributeShouldBeLate
838                                 ( file 
839                                 , node.getName().getPosition()
840                                 , node.getName().getValue()
841                                 )
842                           );
843             }
844             if( node.getOptInitCode() != null )
845             {
846                 mbox.error( new Message.InitializerNotAllowedForNoSetAttribute
847                                 ( file 
848                                 , node.getName().getPosition()
849                                 , node.getName().getValue()
850                                 )
851                           );
852             }
853             if( node.getOptSetCode() != null )
854             {
855                 mbox.error( new Message.SetCodeNotAllowedForNoSetAttribute
856                                 ( file 
857                                 , node.getName().getPosition()
858                                 , node.getName().getValue()
859                                 )
860                           );
861             }
862             if( node.checkModifiers( Modifiers.SETONCE ) )
863             {
864                 mbox.error( new Message.SetOnceNotAllowedForNoSetAttribute
865                                 ( file 
866                                 , node.getName().getPosition()
867                                 , node.getName().getValue()
868                                 )
869                           );
870             }
871         }
872     }
873     
874     public void visitOperationDecl( OperationDecl node )
875     {
876         // check referenced operations
877         int ec = mbox.getErrorCount();
878         visit( node.getOptOperationRefList() );
879         if( mbox.getErrorCount() > ec ) return;
880 
881         // check return type
882         visit( node.getReturnType() );
883         
884         // check that virtual operation is in tree
885         if(    node.isVirtual()
886             && !((Module)node.getParent()).isTree()
887           )
888         {
889             mbox.error( new Message.VirtualOperationCantBeInModule
890                             ( file 
891                             , node.getName().getPosition()
892                             , node.getName().getValue()
893                             )
894                       );
895         }
896         
897         // check parameter names
898         Map/*ID,ParameterDecl*/ nameToParameterMap = new HashMap/*ID,ParameterDecl*/();
899         for( int i = 0; i < node.sizeOptParameterDeclList(); i++ )
900         {
901             ParameterDecl param = node.getOptParameterDeclList( i );
902             ID name = param.getName();
903             if( nameToParameterMap.containsKey( name ) )
904             {
905                 mbox.error( new Message.ParameterAlreadyDeclared
906                                 ( file 
907                                 , node.getName().getPosition()
908                                 , node.getName().getValue()
909                                 )
910                           );
911                 return;
912             }
913             nameToParameterMap.put( name, param );
914         }
915 
916         // check parameters
917         visit( node.getOptParameterDeclList() );
918 
919         if( mbox.getErrorCount() > 0 ) return;
920         
921         ModuleMatrix moduleMatrix = new ModuleMatrix( node, mbox, file ); 
922 
923         // fill parameter variant matrix
924         visit( node.getOptCaseList() );
925 
926         if( mbox.getErrorCount() > 0 ) return;
927 
928         // check that all cases are covered
929         for( Index mi = moduleMatrix.createIndex()
930            ; mi.has()
931            ; mi.next()
932            )
933         {
934             ParameterVariantMatrix paramMatrix = moduleMatrix.getParamMatrix( mi );
935             // covered by referenced operation
936             if( paramMatrix == null ) continue;
937             for( Index pi = paramMatrix.createIndex()
938                ; pi.has()
939                ; pi.next()
940                )
941             {
942                 if( paramMatrix.get( pi ) == null )
943                 {
944                     mbox.error( new Message.CaseNotDefined
945                                             ( file 
946                                             , node.getName().getPosition()
947                                             , paramMatrix.getVariantString( pi )
948                                             )
949                               );
950                 }
951             }
952         }
953     }
954     
955     public void visitNonVirtualParameterDecl( NonVirtualParameterDecl node )
956     {
957         // done in registerOperation
958         // visit( node.getType() );
959     }
960     
961     public void visitVirtualParameterDecl( VirtualParameterDecl node )
962     {
963         // done in registerOperation
964         // visit( node.getType() );
965         
966         ModuleTypeMember type = node.getType().getType();
967         if( type == null ) return;
968         
969         if( type instanceof ConstTypeDecl && ((ConstTypeDecl)type).isFlags() )
970         {
971             mbox.error( new Message.VirtualParameterCantBeOfFlagsType
972                             ( file 
973                             , node.getName().getPosition()
974                             , node.getName().getValue()
975                             )
976                       );
977         }
978     }
979     
980     public void visitOperationRef( OperationRef node )
981     {
982         TDL_Module module;
983         ID optModuleName = node.getOptModuleName();
984         if( optModuleName != null )
985         {
986             module = findBaseModule( optModuleName ); 
987             if( module == null )
988             {
989                 mbox.error( new Message.SynonymNotDefined
990                                         ( file 
991                                         , optModuleName.getPosition()
992                                         , optModuleName.getValue()
993                                         )
994                           );
995                 return;
996             }
997         } else {
998             module = this.module;
999         }
1000         node.setModule( module );
1001         String signature = node.getName().getValue() 
1002                                         + ((OperationDecl)node.getParent()).getParameters();
1003         OperationDecl opDecl = module.findOperation( signature );
1004         if( opDecl == null )
1005         {
1006             mbox.error( new Message.OperationNotFound
1007                                     ( file 
1008                                     , node.getName().getPosition()
1009                                     , optModuleName != null ? optModuleName.getValue() : null
1010                                     , signature
1011                                     )
1012                       );
1013             return;
1014         }
1015         OperationDecl operationDecl = (OperationDecl)node.getParent();
1016         if( !operationDecl.getReturnType().equals( opDecl.getReturnType() ) )
1017         {
1018             mbox.error( new Message.IncompatibleReturnTypes
1019                                     ( file
1020                                     , node.getName().getPosition()
1021                                     , opDecl
1022                                     )
1023                       );
1024         }
1025         
1026         for( int i = 0; i <  operationDecl.sizeOptParameterDeclList(); i++ )
1027         {
1028             ParameterDecl pd = operationDecl.getOptParameterDeclList( i );
1029             if(   (pd instanceof VirtualParameterDecl ? 1 : 0)
1030                 + (opDecl.getOptParameterDeclList(i) instanceof VirtualParameterDecl ? 1 : 0)
1031                 == 1
1032               )
1033             {
1034                 mbox.error( new Message.InconsistentVirtualParameters
1035                                         ( file
1036                                         , pd.getName().getPosition()
1037                                         , opDecl
1038                                         , pd
1039                                         )
1040                           );
1041             }
1042         }
1043         
1044         node.setOperationDecl( opDecl );
1045     }
1046 
1047     public void visitCase( Case node )
1048     {
1049         visit( node.getCaseSignatureList() );
1050     }
1051     
1052     public void visitCaseSignature( CaseSignature node )
1053     {
1054         OperationDecl operationDecl = (OperationDecl)node.getParent().getParent();
1055         
1056         if( operationDecl.sizeOptParameterDeclList() != node.sizeParameterList() )
1057         {
1058             mbox.error( new Message.IncorrectNumberOfParameters
1059                                     ( file 
1060                                     , node.getPosition()
1061                                     , operationDecl.getName().getValue()
1062                                     , operationDecl.getName().getPosition()
1063                                     )
1064                       );
1065             return;
1066         }
1067         
1068         boolean typeNotFound = false;
1069         for( int i = 0; i < node.sizeParameterList(); i++ )
1070         {
1071             ParameterDecl paramDecl = operationDecl.getOptParameterDeclList( i );
1072             Parameter param = node.getParameterList( i );
1073             
1074             visit( param );
1075             
1076             ModuleTypeMember paramType = null;
1077             if( param.getOptType() != null )
1078             {
1079                 paramType = param.getOptType().getType();
1080                 if( paramType == null ) 
1081                 {
1082                     typeNotFound = true;
1083                     continue;
1084                 }
1085             }
1086             
1087             if( paramDecl instanceof VirtualParameterDecl )
1088             {
1089                 ModuleTypeMember paramDeclType 
1090                     = ((VirtualParameterDecl)paramDecl).getType().getType(); 
1091                 if( paramDeclType instanceof ConstTypeDecl )
1092                 {
1093                     if( param.getOptType() != null )
1094                     {
1095                         mbox.error( new Message.TypeNotAllowedForVirtualEnumParameter
1096                                                 ( file 
1097                                                 , node.getPosition()
1098                                                 , paramDecl.getName().getValue()
1099                                                 )
1100                                   );
1101                         return;
1102                     }
1103                     
1104                     if( !TDL_Module.isConstantOfType( param.getName()
1105                                                     , (ConstTypeDecl)paramDeclType 
1106                                                     )
1107                       )
1108                     {
1109                         mbox.error( new Message.ConstantRequiredForVirtualEnumParameter
1110                                                 ( file 
1111                                                 , node.getPosition()
1112                                                 , param.getName().getValue()
1113                                                 , paramDeclType.getName().getValue()
1114                                                 , paramDecl.getName().getValue()
1115                                                 )
1116                                   );
1117                         return;
1118                     }
1119                     continue;
1120                 }
1121                 if( param.getOptType() == null )
1122                 {
1123                     mbox.error( new Message.TypeRequiredForVirtualNodeParameter
1124                                             ( file 
1125                                             , node.getPosition()
1126                                             , paramDecl.getName().getValue()
1127                                             )
1128                               );
1129                     return;
1130                 }
1131                 if( !TDL_Module.isBaseTypeOf( paramDeclType, paramType ) )
1132                 {
1133                     mbox.error( new Message.CaseParameterTypeShouldBeInheritor
1134                                             ( file 
1135                                             , node.getPosition()
1136                                             , param.getOptType().getName().getValue()
1137                                             , param.getName().getValue()
1138                                             , paramDeclType.getName().getValue()
1139                                             )
1140                               );
1141                     return;
1142                 }
1143                 if( ((NodeTypeDecl)paramType).isAbstract() )
1144                 {
1145                     mbox.error( new Message.CaseParameterTypeShouldNotBeAbstract
1146                                             ( file 
1147                                             , node.getPosition()
1148                                             , paramType.getName().getValue()
1149                                             , param.getName().getValue()
1150                                             )
1151                               );
1152                     return;
1153                 }
1154                 if( !paramDecl.getName().equals( param.getName() ) )
1155                 {
1156                     mbox.error( new Message.CaseParameterNameDiffers
1157                                             ( file 
1158                                             , node.getPosition()
1159                                             , operationDecl.getName().getValue()
1160                                             , param.getName().getValue()
1161                                             , paramDecl.getName().getValue()
1162                                             , paramDecl.getName().getPosition()
1163                                             )
1164                               );
1165                     return;
1166                 }
1167             } else {
1168                 if( param.getOptType() != null )
1169                 {
1170                     mbox.error( new Message.CaseParameterTypeNotAllowed
1171                                             ( file 
1172                                             , node.getPosition()
1173                                             , param.getName().getValue()
1174                                             )
1175                               );
1176                     return;
1177                 }
1178                 if( !paramDecl.getName().equals( param.getName() ) )
1179                 {
1180                     mbox.error( new Message.CaseParameterNameDiffers( file 
1181                                                , node.getPosition()
1182                                                , operationDecl.getName().getValue()
1183                                                , param.getName().getValue()
1184                                                , paramDecl.getName().getValue()
1185                                                , paramDecl.getName().getPosition()
1186                                                )
1187                               );
1188                     return;
1189                 }
1190             }
1191         }
1192         if( typeNotFound ) return;
1193         CaseSignature signature = operationDecl.getModuleMatrix().addCase( node );
1194         if( signature != null )
1195         {
1196             mbox.error( new Message.CaseAlreadyDefined
1197                                     ( file 
1198                                     , node.getPosition()
1199                                     , signature
1200                                     )
1201                       );
1202             return;
1203         }
1204     }
1205     
1206     public void visitParameter( Parameter node )
1207     {
1208         visit( node.getOptType() );
1209     }
1210 
1211     public void visitNodeType( NodeType node )
1212     {
1213         visit( node.getType() );
1214     }
1215 
1216     public void visitPredefinedType( PredefinedType node )
1217     {
1218         // no check
1219     }
1220 
1221     public void visitNameType( NameType node )
1222     {
1223         // no check
1224     }
1225 
1226     public void visitTypeRef( TypeRef node )
1227     {
1228         TDL_Module module;
1229         ID optModuleName = node.getOptModuleName();
1230         if( optModuleName != null )
1231         {
1232             module = findBaseModule( optModuleName ); 
1233             if( module == null )
1234             {
1235                 mbox.error( new Message.SynonymNotDefined( file 
1236                                            , optModuleName.getPosition()
1237                                            , optModuleName.getValue()
1238                                            )
1239                           );
1240                 return;
1241             }
1242         } else {
1243             module = this.module;
1244         }
1245         node.setModule( module );
1246         ModuleTypeMember member = module.findModuleTypeMember( node.getName().getValue() );
1247         if( member == null )
1248         {
1249             mbox.error( new Message.TypeNotFound( file 
1250                                        , node.getName().getPosition()
1251                                        , optModuleName != null ? optModuleName.getValue() : null
1252                                        , node.getName().getValue()
1253                                        )
1254                       );
1255             return;
1256         }
1257         node.setType( member );
1258     }
1259 
1260     public void visitID( ID node )
1261     {
1262         throw new UnsupportedOperationException();
1263     }
1264     
1265     public void visitQID( QID node )
1266     {
1267         throw new UnsupportedOperationException();
1268     }
1269     
1270     public void visitCode( Code node )
1271     {
1272         throw new UnsupportedOperationException();
1273     }
1274 }