001    // This file is part of the RECODER library and protected by the LGPL.
002    
003    package recoder.java.declaration;
004    
005    import recoder.java.Identifier;
006    import recoder.java.ProgramElement;
007    import recoder.java.SourceVisitor;
008    import recoder.java.Statement;
009    import recoder.java.StatementBlock;
010    import recoder.java.StatementContainer;
011    import recoder.list.generic.ASTList;
012    
013    /**
014     * Outer or member class declaration. There are several types of class
015     * declarations:
016     * <ul>
017     * <li>outer classes
018     * <ul>
019     * <li>getContainer() instanceof Package
020     * <li>getStatementContainer() == null
021     * <li>getName() != null
022     * </ul>
023     * <li>member classes
024     * <ul>
025     * <li>getContainer() instanceof TypeDeclaration
026     * <li>getStatementContainer() == null
027     * <li>getName() != null
028     * </ul>
029     * <li>inner classes
030     * <ul>
031     * <li>as member classes, and
032     * <li>not declared "static" (explicitly or implicitly)
033     * </ul>
034     * <li>local classes
035     * <ul>
036     * <li>getContainer() != null (either Method or ClassInitializer)
037     * <li>getStatementContainer() != null
038     * <li>getName() != null
039     * </ul>
040     * <li>anonymous classes
041     * <ul>
042     * <li>getContainer() != null (but further unspecified; 
043     *                                                              could be Method, ClassInitializer, but also Package in case
044     *                                                              it appears in )
045     * <li>getStatementContainer() == null
046     * <li>getName() == null
047     * </ul>
048     * </ul>
049     * Anonymous local classes have exactly one supertype and no subtypes. <BR>
050     * Binary classes may have only binary members.
051     */
052    public class ClassDeclaration extends TypeDeclaration implements Statement {
053    
054        /**
055             * serialization id
056             */
057            private static final long serialVersionUID = -1520369925548201696L;
058    
059            /**
060         * Extending.
061         */
062            private Extends extending;
063        
064        /**
065         * Type Parameters (Java 5)
066         */
067            private ASTList<TypeParameterDeclaration> typeParameters;
068    
069        /**
070         * Implementing.
071         */
072            private Implements implementing;
073    
074        /**
075         * Class declaration.
076         */
077        public ClassDeclaration() {
078            // nothing to do here
079        }
080    
081        /** Construct a non-anonymous class. */
082        public ClassDeclaration(ASTList<DeclarationSpecifier> declSpecs, Identifier name, Extends extended, Implements implemented,
083                    ASTList<MemberDeclaration> members, ASTList<TypeParameterDeclaration> typeParameters) {
084            super(declSpecs, name);
085            setExtendedTypes(extended);
086            setImplementedTypes(implemented);
087            setMembers(members);
088            setTypeParameters(typeParameters);
089            makeParentRoleValid();
090        }
091        
092        public ClassDeclaration(ASTList<DeclarationSpecifier> declSpecs, Identifier name, Extends extended, Implements implemented,
093                    ASTList<MemberDeclaration> members) {
094            this (declSpecs, name, extended, implemented, members, null);
095        }
096    
097        /**
098         * Class declaration.
099         * 
100         * @param members
101         *            a member declaration mutable list.
102         */
103        public ClassDeclaration(ASTList<MemberDeclaration> members) {
104            setMembers(members);
105            makeParentRoleValid();
106        }
107    
108        /**
109         * Class declaration.
110         * 
111         * @param proto
112         *            a class declaration.
113         */
114        protected ClassDeclaration(ClassDeclaration proto) {
115            super(proto);
116            if (proto.extending != null) {
117                extending = proto.extending.deepClone();
118            }
119            if (proto.implementing != null) {
120                implementing = proto.implementing.deepClone();
121            }
122            if (proto.typeParameters != null) {
123                    typeParameters = proto.typeParameters.deepClone();
124            }
125            makeParentRoleValid();
126        }
127    
128        /**
129         * Deep clone.
130         * 
131         * @return the object.
132         */
133        public ClassDeclaration deepClone() {
134            return new ClassDeclaration(this);
135        }
136    
137        /**
138         * Make parent role valid.
139         */
140        public void makeParentRoleValid() {
141            super.makeParentRoleValid();
142            if (extending != null) {
143                extending.setParent(this);
144            }
145            if (implementing != null) {
146                implementing.setParent(this);
147            }
148            if (typeParameters != null) {
149                    for (TypeParameterDeclaration tp : typeParameters) {
150                            tp.setParent(this);
151                    }
152            }
153        }
154    
155        /**
156         * Returns the number of children of this node.
157         * 
158         * @return an int giving the number of children of this node
159         */
160        public int getChildCount() {
161            int result = 0;
162            if (declarationSpecifiers != null)
163                result += declarationSpecifiers.size();
164            if (name != null)
165                result++;
166            if (extending != null)
167                result++;
168            if (implementing != null)
169                result++;
170            if (members != null)
171                result += members.size();
172            if (typeParameters != null)
173                    result += typeParameters.size();
174            return result;
175        }
176    
177        /**
178         * Returns the child at the specified index in this node's "virtual" child
179         * array
180         * 
181         * @param index
182         *            an index into this node's "virtual" child array
183         * @return the program element at the given position
184         * @exception ArrayIndexOutOfBoundsException
185         *                if <tt>index</tt> is out of bounds
186         */
187        public ProgramElement getChildAt(int index) {
188            int len;
189            if (declarationSpecifiers != null) {
190                len = declarationSpecifiers.size();
191                if (len > index) {
192                    return declarationSpecifiers.get(index);
193                }
194                index -= len;
195            }
196            if (name != null) {
197                if (index == 0)
198                    return name;
199                index--;
200            }
201            if (typeParameters != null) {
202                    len = typeParameters.size();
203                    if (len > index)
204                            return typeParameters.get(index);
205                    index -= len;
206            }
207            if (extending != null) {
208                if (index == 0)
209                    return extending;
210                index--;
211            }
212            if (implementing != null) {
213                if (index == 0)
214                    return implementing;
215                index--;
216            }
217            if (members != null) {
218                    if (index < members.size())
219                            return members.get(index);
220                    index -= members.size();
221            }
222            throw new ArrayIndexOutOfBoundsException();
223        }
224    
225        public int getChildPositionCode(ProgramElement child) {
226            // role 0 (IDX): declaration specifier
227            // role 1: identifier
228            // role 2: extends
229            // role 3: implements (no occurrence in interfaces)
230            // role 4 (IDX): members
231            // role 5 (IDX): type parameters
232            if (declarationSpecifiers != null) {
233                int index = declarationSpecifiers.indexOf(child);
234                if (index >= 0) {
235                    return (index << 4) | 0;
236                }
237            }
238            if (name == child)
239                return 1;
240            if (extending == child)
241                return 2;
242            if (implementing == child)
243                return 3;
244            if (members != null) {
245                int index = members.indexOf(child);
246                if (index >= 0) {
247                    return (index << 4) | 4;
248                }
249            }
250            if (typeParameters != null) {
251                    int index = typeParameters.size();
252                    if (index >= 0) {
253                            return (index << 4) | 5;
254                    }
255            }
256            return -1;
257        }
258    
259        /**
260         * Replace a single child in the current node. The child to replace is
261         * matched by identity and hence must be known exactly. The replacement
262         * element can be null - in that case, the child is effectively removed. The
263         * parent role of the new child is validated, while the parent link of the
264         * replaced child is left untouched.
265         * 
266         * @param p
267         *            the old child.
268         * @param p
269         *            the new child.
270         * @return true if a replacement has occured, false otherwise.
271         * @exception ClassCastException
272         *                if the new child cannot take over the role of the old one.
273         */
274        public boolean replaceChild(ProgramElement p, ProgramElement q) {
275            if (p == null) {
276                throw new NullPointerException();
277            }
278            int count;
279            count = (declarationSpecifiers == null) ? 0 : declarationSpecifiers.size();
280            for (int i = 0; i < count; i++) {
281                if (declarationSpecifiers.get(i) == p) {
282                    if (q == null) {
283                        declarationSpecifiers.remove(i);
284                    } else {
285                        DeclarationSpecifier r = (DeclarationSpecifier) q;
286                        declarationSpecifiers.set(i, r);
287                        r.setParent(this);
288                    }
289                    return true;
290                }
291            }
292            if (name == p) {
293                Identifier r = (Identifier) q;
294                name = r;
295                if (r != null) {
296                    r.setParent(this);
297                }
298                return true;
299            }
300            if (extending == p) {
301                Extends r = (Extends) q;
302                extending = r;
303                if (r != null) {
304                    r.setParent(this);
305                }
306                return true;
307            }
308            if (implementing == p) {
309                Implements r = (Implements) q;
310                implementing = r;
311                if (r != null) {
312                    r.setParent(this);
313                }
314                return true;
315            }
316            count = (members == null) ? 0 : members.size();
317            for (int i = 0; i < count; i++) {
318                if (members.get(i) == p) {
319                    if (q == null) {
320                        members.remove(i);
321                    } else {
322                        MemberDeclaration r = (MemberDeclaration) q;
323                        members.set(i, r);
324                        r.setMemberParent(this);
325                    }
326                    return true;
327                }
328            }
329            if (typeParameters != null) {
330                    int idx = typeParameters.indexOf(p);
331                    if (idx != -1) {
332                            if (q == null) {
333                                    typeParameters.remove(idx);
334                            } else {
335                                    TypeParameterDeclaration r = (TypeParameterDeclaration)q;
336                                    typeParameters.set(idx, r);
337                                    r.setParent(this);
338                            }
339                            return true;
340                    }
341            }
342            return false;
343        }
344    
345        /**
346         * Get statement container.
347         * 
348         * @return null, if the type is not declared locally.
349         */
350        public StatementContainer getStatementContainer() {
351            return (parent instanceof StatementContainer) ? (StatementContainer) parent : null;
352        }
353    
354        /**
355         * Set statement container. Must be a {@link recoder.java.StatementBlock}.
356         * 
357         * @param p
358         *            a statement container.
359         */
360        public void setStatementContainer(StatementContainer p) {
361            parent = (StatementBlock) p;
362        }
363    
364        /**
365         * Get extended types.
366         * 
367         * @return the extends.
368         */
369        public Extends getExtendedTypes() {
370            return extending;
371        }
372    
373        /**
374         * Set extended types.
375         * 
376         * @param spec
377         *            an extends.
378         */
379        public void setExtendedTypes(Extends spec) {
380            extending = spec;
381        }
382    
383        /**
384         * Get implemented types.
385         * 
386         * @return the implements.
387         */
388        public Implements getImplementedTypes() {
389            return implementing;
390        }
391    
392        /**
393         * Set implemented types.
394         * 
395         * @param spec
396         *            an implements.
397         */
398        public void setImplementedTypes(Implements spec) {
399            implementing = spec;
400        }
401    
402        /**
403         * Classes are never strictfp.
404         */
405        public boolean isStrictFp() {
406            return false;
407        }
408    
409        /**
410         * Classes are never transient.
411         */
412        public boolean isTransient() {
413            return false;
414        }
415    
416        /**
417         * Classes are never volatile.
418         */
419        public boolean isVolatile() {
420            return false;
421        }
422    
423        public boolean isInterface() {
424            return false;
425        }
426        
427        public boolean isOrdinaryInterface() {
428            return false;
429        }
430        
431        public boolean isAnnotationType() {
432            return false;
433        }
434    
435        public boolean isEnumType() {
436            return false;
437        }
438    
439        public boolean isOrdinaryClass() {
440            return true;
441        }
442    
443        public void accept(SourceVisitor v) {
444            v.visitClassDeclaration(this);
445        }
446        
447        public void setTypeParameters(ASTList<TypeParameterDeclaration> typeParameters) {
448            this.typeParameters = typeParameters;
449        }
450        
451        public ASTList<TypeParameterDeclaration> getTypeParameters() {
452            return typeParameters;
453        }
454    
455            public boolean isInner() {
456                    if (isStatic()) 
457                            return false;
458                    if (!(getContainingClassType() instanceof ClassDeclaration))
459                            return false;
460                    if (getName() == null)
461                            return false;
462                    if (getStatementContainer() != null)
463                            return false;
464                    return true;
465            }
466    }