Source Code Cross Referenced for QueryTag.java in  » Web-Framework » makumba » org » makumba » list » tags » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Framework » makumba » org.makumba.list.tags 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        ///////////////////////////////
002:        //  Makumba, Makumba tag library
003:        //  Copyright (C) 2000-2003  http://www.makumba.org
004:        //
005:        //  This library is free software; you can redistribute it and/or
006:        //  modify it under the terms of the GNU Lesser General Public
007:        //  License as published by the Free Software Foundation; either
008:        //  version 2.1 of the License, or (at your option) any later version.
009:        //
010:        //  This library is distributed in the hope that it will be useful,
011:        //  but WITHOUT ANY WARRANTY; without even the implied warranty of
012:        //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013:        //  Lesser General Public License for more details.
014:        //
015:        //  You should have received a copy of the GNU Lesser General Public
016:        //  License along with this library; if not, write to the Free Software
017:        //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:        //
019:        //  -------------
020:        //  $Id: QueryTag.java 1950 2007-10-26 19:50:01Z rosso_nero $
021:        //  $Name$
022:        /////////////////////////////////////
023:
024:        package org.makumba.list.tags;
025:
026:        import javax.servlet.ServletRequest;
027:        import javax.servlet.jsp.JspException;
028:        import javax.servlet.jsp.PageContext;
029:        import javax.servlet.jsp.tagext.IterationTag;
030:
031:        import org.makumba.LogicException;
032:        import org.makumba.MakumbaError;
033:        import org.makumba.MakumbaSystem;
034:        import org.makumba.ProgrammerError;
035:        import org.makumba.analyser.AnalysableTag;
036:        import org.makumba.analyser.PageCache;
037:        import org.makumba.commons.MakumbaJspAnalyzer;
038:        import org.makumba.commons.MultipleKey;
039:        import org.makumba.commons.RuntimeWrappedException;
040:        import org.makumba.commons.formatters.RecordFormatter;
041:        import org.makumba.list.engine.ComposedQuery;
042:        import org.makumba.list.engine.ComposedSubquery;
043:        import org.makumba.list.engine.QueryExecution;
044:        import org.makumba.list.engine.valuecomputer.ValueComputer;
045:        import org.makumba.list.html.RecordViewer;
046:
047:        /**
048:         * Display of OQL query results in nested loops. The Query FROM, WHERE, GROUPBY and ORDERBY are indicated in the head of
049:         * the tag. The query projections are indicated by Value tags in the body of the tag. The sub-tags will generate
050:         * subqueries of their enclosing tag queries (i.e. their WHERE, GROUPBY and ORDERBY are concatenated). Attributes of the
051:         * environment can be passed as $attrName to the query
052:         * 
053:         * @author Cristian Bogdan
054:         * @version $Id: QueryTag.java 1950 2007-10-26 19:50:01Z rosso_nero $
055:         */
056:        public class QueryTag extends GenericListTag implements  IterationTag {
057:
058:            private static final long serialVersionUID = 1L;
059:
060:            String[] queryProps = new String[5];
061:
062:            String separator = "";
063:
064:            String countVar;
065:
066:            String maxCountVar;
067:
068:            String offset, limit;
069:
070:            static String standardCountVar = "org_makumba_view_jsptaglib_countVar";
071:
072:            static String standardMaxCountVar = "org_makumba_view_jsptaglib_maxCountVar";
073:
074:            static String standardLastCountVar = "org_makumba_view_jsptaglib_lastCountVar";
075:
076:            static String standardMaxResultsVar = "org_makumba_view_jsptaglib_MaxResultsVar";
077:
078:            static String standardMaxResultsContext = "org_makumba_view_jsptaglib_MaxResultsContext";
079:
080:            static String standardMaxResultsKey = "org_makumba_view_jsptaglib_MaxResultsKey";
081:
082:            public void setFrom(String s) {
083:                queryProps[ComposedQuery.FROM] = s;
084:            }
085:
086:            public void setVariableFrom(String s) {
087:                queryProps[ComposedQuery.VARFROM] = s;
088:            }
089:
090:            public void setWhere(String s) {
091:                queryProps[ComposedQuery.WHERE] = s;
092:            }
093:
094:            public void setOrderBy(String s) {
095:                queryProps[ComposedQuery.ORDERBY] = s;
096:            }
097:
098:            public void setGroupBy(String s) {
099:                queryProps[ComposedQuery.GROUPBY] = s;
100:            }
101:
102:            public void setSeparator(String s) {
103:                separator = s;
104:            }
105:
106:            public void setCountVar(String s) {
107:                countVar = s;
108:            }
109:
110:            public void setMaxCountVar(String s) {
111:                maxCountVar = s;
112:            }
113:
114:            public void setOffset(String s) throws JspException {
115:                onlyOuterListArgument("offset");
116:                onlyInt("offset", s);
117:                offset = s.trim();
118:            }
119:
120:            public void setLimit(String s) throws JspException {
121:                onlyOuterListArgument("limit");
122:                onlyInt("limit", s);
123:                limit = s.trim();
124:            }
125:
126:            protected void onlyOuterListArgument(String s) throws JspException {
127:                QueryTag t = (QueryTag) findAncestorWithClass(this ,
128:                        QueryTag.class);
129:                while (t != null && t instanceof  ObjectTag)
130:                    t = (QueryTag) findAncestorWithClass(t, QueryTag.class);
131:                if (t instanceof  QueryTag)
132:                    throw new RuntimeWrappedException(
133:                            new MakumbaJspException(
134:                                    this ,
135:                                    "the "
136:                                            + s
137:                                            + " parameter can only be set for the outermost mak:list tag"));
138:            }
139:
140:            protected void onlyInt(String s, String value) throws JspException {
141:                value = value.trim();
142:                if (value.startsWith("$"))
143:                    return;
144:                try {
145:                    Integer.parseInt(value);
146:                } catch (NumberFormatException nfe) {
147:                    throw new RuntimeWrappedException(
148:                            new MakumbaJspException(
149:                                    this ,
150:                                    "the "
151:                                            + s
152:                                            + " parameter can only be an $attribute or an int"));
153:
154:                }
155:            }
156:
157:            // runtime stuff
158:            QueryExecution execution;
159:
160:            /**
161:             * Computes and set the tagKey. At analisys time, the listQuery is associated with the tagKey, and retrieved at
162:             * runtime. At runtime, the QueryExecution is discovered by the tag based on the tagKey.
163:             * 
164:             * @param pageCache
165:             *            The page cache for the current page
166:             */
167:            public void setTagKey(PageCache pageCache) {
168:                tagKey = new MultipleKey(queryProps.length + 2);
169:                for (int i = 0; i < queryProps.length; i++)
170:                    tagKey.setAt(queryProps[i], i);
171:
172:                // if we have a parent, we append the key of the parent
173:                tagKey.setAt(getParentListKey(this , pageCache),
174:                        queryProps.length);
175:                tagKey.setAt(id, queryProps.length + 1);
176:            }
177:
178:            /**
179:             * Determines whether the tag can have the same key as others in the page
180:             * 
181:             * @return <code>true</code> if the tag is allowed to have the same key as others in the page, <code>false</code>
182:             *         otherwise
183:             */
184:            public boolean allowsIdenticalKey() {
185:                return false;
186:            }
187:
188:            /**
189:             * Starts the analysis of the tag, without knowing what tags follow it in the page. Defines a query, sets the types
190:             * of variables to "int".
191:             * 
192:             * @param pageCache
193:             *            The page cache for the current page
194:             */
195:            public void doStartAnalyze(PageCache pageCache) {
196:                // check whether we have an $.. in the order by (not supported, only #{..} is allowed
197:                String orderBy = queryProps[ComposedQuery.ORDERBY];
198:                if (orderBy != null && orderBy.indexOf("$") != -1) {
199:                    throw new ProgrammerError(
200:                            "Illegal use of an $attribute orderBy: '"
201:                                    + orderBy
202:                                    + "' ==> only JSP Expression Language using #{..} is allowed!");
203:                }
204:
205:                // we make ComposedQuery cache our query
206:                QueryTag.cacheQuery(pageCache, tagKey, queryProps,
207:                        getParentListKey(this , pageCache));
208:
209:                if (countVar != null)
210:                    setType(pageCache, countVar, MakumbaSystem.makeFieldOfType(
211:                            countVar, "int"));
212:
213:                if (maxCountVar != null)
214:                    setType(pageCache, maxCountVar, MakumbaSystem
215:                            .makeFieldOfType(maxCountVar, "int"));
216:            }
217:
218:            /**
219:             * Ends the analysis of the tag, after all tags in the page were visited. As all the query projections are known, a
220:             * RecordViewer is cached as formatter for the mak:values nested in this tag.
221:             * 
222:             * @param pageCache
223:             *            The page cache for the current page
224:             */
225:            public void doEndAnalyze(PageCache pageCache) {
226:                ComposedQuery cq = QueryTag.getQuery(pageCache, tagKey);
227:                cq.analyze();
228:                pageCache.cache(RecordFormatter.FORMATTERS, tagKey,
229:                        new RecordViewer(cq));
230:            }
231:
232:            static final Integer zero = new Integer(0);
233:
234:            static final Integer one = new Integer(1);
235:
236:            Object upperCount = null;
237:
238:            Object upperMaxCount = null;
239:
240:            ValueComputer choiceComputer;
241:
242:            private static ThreadLocal<ServletRequest> servletRequestThreadLocal = new ThreadLocal<ServletRequest>();
243:
244:            /**
245:             * Decides if there will be any tag iteration. The QueryExecution is found (and made if needed), and we check if
246:             * there are any results in the iterationGroup.
247:             * 
248:             * @param pageCache
249:             *            The page cache for the current page
250:             * @return The tag return state as defined in the {@link javax.servlet.jsp.tagext.Tag} interface
251:             * @see QueryExecution
252:             */
253:            public int doAnalyzedStartTag(PageCache pageCache)
254:                    throws LogicException, JspException {
255:                servletRequestThreadLocal.set(pageContext.getRequest());
256:                if (getParentList(this ) == null)
257:                    QueryExecution.startListGroup(pageContext);
258:                else {
259:                    upperCount = pageContext.getRequest().getAttribute(
260:                            standardCountVar);
261:                    upperMaxCount = pageContext.getRequest().getAttribute(
262:                            standardMaxCountVar);
263:                }
264:
265:                execution = QueryExecution.getFor(tagKey, pageContext, offset,
266:                        limit);
267:
268:                int n = execution.onParentIteration();
269:
270:                setNumberOfIterations(n);
271:
272:                // set the total result count, i.e. the count this list would have w/o limit & offset
273:                int maxResults = Integer.MIN_VALUE;
274:                int limitEval = QueryExecution.computeLimit(pageContext, limit,
275:                        -1);
276:                int offsetEval = QueryExecution.computeLimit(pageContext,
277:                        offset, 0);
278:                if ((offsetEval == 0 && limitEval == -1)
279:                        || (offsetEval == 0 && (limitEval > 00 && limitEval < n))) {
280:                    // we can set the total count if there is no limit / offset in the page
281:                    maxResults = n;
282:                } else {
283:                    // otherwise we need to make a new query
284:                    // we only prepare the query, but do not run it, that will happen on demand in the method getting the result
285:                    ComposedQuery query = null;
286:                    String[] simpleQueryProps = queryProps.clone();
287:                    simpleQueryProps[ComposedQuery.ORDERBY] = "";
288:                    MultipleKey maxResultsKey = getMaxResultsKey(tagKey);
289:                    MultipleKey parentKey = getParentListKey(this , pageCache);
290:                    String ql = (String) pageCache.retrieve(
291:                            MakumbaJspAnalyzer.QUERY_LANGUAGE,
292:                            MakumbaJspAnalyzer.QUERY_LANGUAGE);
293:                    query = parentKey == null ? new ComposedQuery(
294:                            simpleQueryProps, ql) : new ComposedSubquery(
295:                            simpleQueryProps, QueryTag.getQuery(pageCache,
296:                                    parentKey), ql);
297:                    query.addProjection("count(*)");
298:                    query.init();
299:                    pageCache.cache(GenericListTag.QUERY, maxResultsKey, query);
300:                    // we need to pass these variables in request to the method doing the query
301:                    // TODO: this looks like a hack, and might not be safe if there are more lists in the same page
302:                    pageContext.getRequest().setAttribute(
303:                            standardMaxResultsContext, pageContext);
304:                    pageContext.getRequest().setAttribute(
305:                            standardMaxResultsKey, maxResultsKey);
306:                }
307:                pageContext.getRequest().setAttribute(standardMaxResultsVar,
308:                        maxResults);
309:
310:                if (n > 0) {
311:                    if (countVar != null)
312:                        pageContext.setAttribute(countVar, one);
313:                    pageContext.getRequest()
314:                            .setAttribute(standardCountVar, one);
315:                    return EVAL_BODY_INCLUDE;
316:                }
317:                if (countVar != null)
318:                    pageContext.setAttribute(countVar, zero);
319:                pageContext.getRequest().setAttribute(standardCountVar, zero);
320:                return SKIP_BODY;
321:            }
322:
323:            private MultipleKey getMaxResultsKey(MultipleKey tagKey) {
324:                MultipleKey totalKey = (MultipleKey) tagKey.clone();
325:                totalKey.add(standardMaxResultsVar);
326:                return totalKey;
327:            }
328:
329:            /**
330:             * Sets the number of iterations in the iterationGroup. ObjectTag will redefine this and throw an exception if n>1
331:             * 
332:             * @param n
333:             *            The number of iterations in the iterationGroup
334:             * @throws JspException
335:             * @see ObjectTag
336:             */
337:            protected void setNumberOfIterations(int n) throws JspException {
338:                Integer cnt = new Integer(n);
339:                if (maxCountVar != null)
340:                    pageContext.setAttribute(maxCountVar, cnt);
341:                pageContext.getRequest().setAttribute(standardMaxCountVar, cnt);
342:            }
343:
344:            /**
345:             * Decides whether to do further iterations. Checks if we got to the end of the iterationGroup.
346:             * 
347:             * @return The tag return state as defined in the {@link javax.servlet.jsp.tagext.Tag} interface
348:             * @throws JspException
349:             */
350:            public int doAfterBody() throws JspException {
351:                runningTag.set(tagData);
352:                try {
353:
354:                    int n = execution.nextGroupIteration();
355:
356:                    if (n != -1) {
357:                        // print the separator
358:                        try {
359:                            pageContext.getOut().print(separator);
360:                        } catch (Exception e) {
361:                            throw new MakumbaJspException(e);
362:                        }
363:
364:                        Integer cnt = new Integer(n + 1);
365:                        if (countVar != null)
366:                            pageContext.setAttribute(countVar, cnt);
367:                        pageContext.getRequest().setAttribute(standardCountVar,
368:                                cnt);
369:                        return EVAL_BODY_AGAIN;
370:                    }
371:                    return SKIP_BODY;
372:                } finally {
373:                    runningTag.set(null);
374:                }
375:            }
376:
377:            /**
378:             * Cleans up variables, especially for the rootList.
379:             * 
380:             * @param pageCache
381:             *            The page cache for the current page
382:             * @return The tag return state as defined in the {@link javax.servlet.jsp.tagext.Tag} interface in order to
383:             *         continue evaluating the page.
384:             * @throws JspException
385:             */
386:            public int doAnalyzedEndTag(PageCache pageCache)
387:                    throws JspException {
388:                pageContext.getRequest().setAttribute(
389:                        standardLastCountVar,
390:                        pageContext.getRequest().getAttribute(
391:                                standardMaxCountVar));
392:
393:                pageContext.getRequest().setAttribute(standardCountVar,
394:                        upperCount);
395:                pageContext.getRequest().setAttribute(standardMaxCountVar,
396:                        upperMaxCount);
397:                execution.endIterationGroup();
398:
399:                if (getParentList(this ) == null)
400:                    QueryExecution.endListGroup(pageContext);
401:
402:                execution = null;
403:                queryProps[0] = queryProps[1] = queryProps[2] = queryProps[3] = null;
404:                countVar = maxCountVar = null;
405:                separator = "";
406:                return EVAL_PAGE;
407:            }
408:
409:            /**
410:             * Finds the parentList of a list
411:             * 
412:             * @param tag
413:             *            the tag we want to discover the parent of
414:             * @return the parent QueryTag of the Tag
415:             */
416:            public static AnalysableTag getParentList(AnalysableTag tag) {
417:                return (AnalysableTag) findAncestorWithClass(tag,
418:                        QueryTag.class);
419:            }
420:
421:            /**
422:             * Finds the key of the parentList of the Tag
423:             * 
424:             * @param tag
425:             *            the tag we want to discover the parent of
426:             * @param pageCache
427:             *            the pageCache of the current page
428:             * @return The MultipleKey identifying the parentList
429:             */
430:            public static MultipleKey getParentListKey(AnalysableTag tag,
431:                    PageCache pageCache) {
432:                AnalysableTag parentList = getParentList(tag);
433:                return parentList == null ? null : parentList.getTagKey();
434:            }
435:
436:            /**
437:             * Gets the query for a given key
438:             * 
439:             * @param key
440:             *            the key of the tag for which we want to get a query
441:             * @return The OQL query corresponding to this tag
442:             */
443:            public static ComposedQuery getQuery(PageCache pc, MultipleKey key) {
444:                ComposedQuery ret = (ComposedQuery) pc.retrieve(
445:                        GenericListTag.QUERY, key);
446:                if (ret == null)
447:                    throw new MakumbaError("unknown query for key " + key);
448:                return ret;
449:            }
450:
451:            /**
452:             * Gets a composed query from the cache, and if none is found, creates one and caches it.
453:             * 
454:             * @param key
455:             *            the key of the tag
456:             * @param sections
457:             *            the sections needed to compose a query
458:             * @param parentKey
459:             *            the key of the parent tag, if any
460:             */
461:            public static ComposedQuery cacheQuery(PageCache pc,
462:                    MultipleKey key, String[] sections, MultipleKey parentKey) {
463:                ComposedQuery ret = (ComposedQuery) pc.retrieve(
464:                        GenericListTag.QUERY, key);
465:                if (ret != null)
466:                    return ret;
467:                String ql = (String) pc.retrieve(
468:                        MakumbaJspAnalyzer.QUERY_LANGUAGE,
469:                        MakumbaJspAnalyzer.QUERY_LANGUAGE);
470:                ret = parentKey == null ? new ComposedQuery(sections, ql)
471:                        : new ComposedSubquery(sections, QueryTag.getQuery(pc,
472:                                parentKey), ql);
473:
474:                ret.init();
475:                pc.cache(GenericListTag.QUERY, key, ret);
476:                return ret;
477:            }
478:
479:            /**
480:             * Gives the value of the iteration in progress
481:             * 
482:             * @return The current count of iterations
483:             */
484:            public static int count() {
485:                Object countAttr = servletRequestThreadLocal.get()
486:                        .getAttribute(standardCountVar);
487:                if (countAttr == null) {
488:                    // throw new ProgrammerError("mak:count() can only be used inside a <mak:list> tag");
489:                    return -1;
490:                }
491:                return ((Integer) countAttr).intValue();
492:            }
493:
494:            /**
495:             * Gives the maximum number of iteration of the iterationGroup
496:             * 
497:             * @return The maximum number of iterations within the current iterationGroup
498:             */
499:            public static int maxCount() {
500:                Object maxAttr = servletRequestThreadLocal.get().getAttribute(
501:                        standardMaxCountVar);
502:                if (maxAttr == null) {
503:                    // throw new ProgrammerError("mak:maxCount() can only be used inside a <mak:list> tag");
504:                    return -1;
505:                }
506:                return ((Integer) maxAttr).intValue();
507:            }
508:
509:            /**
510:             * Gives the maximum number of results returned as if the query would not contain any limit / offset. <br>
511:             * TODO: we need to pass quite some information in the request attributes, as this method has to be static. Not sure
512:             * what happens if there are more lists in the same page, if that would overlap or not.
513:             * 
514:             * @return The maximum number of results returned as if the query would not contain any limit / offset.
515:             */
516:            public static int maxResults() {
517:                ServletRequest servletRequest = servletRequestThreadLocal.get();
518:                Object totalAttr = servletRequest
519:                        .getAttribute(standardMaxResultsVar);
520:                if (totalAttr == null) {
521:                    // throw new ProgrammerError("mak:maxResults() can only be used inside a <mak:list> tag");
522:                    return -1;
523:                }
524:
525:                Integer total = ((Integer) totalAttr);
526:                if (total == Integer.MIN_VALUE) { // we still need to evaluate this total count
527:                    PageContext pageContext = (PageContext) servletRequest
528:                            .getAttribute(standardMaxResultsContext);
529:                    MultipleKey keyMaxResults = (MultipleKey) servletRequest
530:                            .getAttribute(standardMaxResultsKey);
531:                    try {
532:                        QueryExecution exec = QueryExecution.getFor(
533:                                keyMaxResults, pageContext, null, null);
534:                        exec.getIterationGroupData();
535:                        total = ((Integer) exec.currentListData().get("col1"));
536:                        servletRequest.setAttribute(standardMaxResultsVar,
537:                                total);
538:                    } catch (LogicException e) {
539:                        e.printStackTrace();
540:                        throw new RuntimeWrappedException(e);
541:                    }
542:                }
543:                return total;
544:            }
545:
546:            /**
547:             * Gives the total number of iterations of the previous iterationGroup
548:             * 
549:             * @return The total number of iterations performed within the previous iterationGroup
550:             */
551:            public static int lastCount() {
552:                if (servletRequestThreadLocal.get() == null)
553:                    return -1;
554:                return ((Integer) servletRequestThreadLocal.get().getAttribute(
555:                        standardLastCountVar)).intValue();
556:            }
557:
558:            @Override
559:            public boolean canHaveBody() {
560:                return true;
561:            }
562:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.