Source Code Cross Referenced for ClassParser.java in  » Code-Analyzer » findbugs » edu » umd » cs » findbugs » classfile » engine » 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 » Code Analyzer » findbugs » edu.umd.cs.findbugs.classfile.engine 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * FindBugs - Find Bugs in Java programs
003:         * Copyright (C) 2006, University of Maryland
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:        package edu.umd.cs.findbugs.classfile.engine;
021:
022:        import java.io.DataInputStream;
023:        import java.io.IOException;
024:        import java.util.Collection;
025:        import java.util.HashSet;
026:        import java.util.Set;
027:
028:        import edu.umd.cs.findbugs.annotations.CheckForNull;
029:        import edu.umd.cs.findbugs.classfile.ClassDescriptor;
030:        import edu.umd.cs.findbugs.classfile.DescriptorFactory;
031:        import edu.umd.cs.findbugs.classfile.FieldDescriptor;
032:        import edu.umd.cs.findbugs.classfile.IClassConstants;
033:        import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
034:        import edu.umd.cs.findbugs.classfile.InvalidClassFileFormatException;
035:        import edu.umd.cs.findbugs.classfile.MethodDescriptor;
036:        import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
037:        import edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo;
038:        import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
039:        import edu.umd.cs.findbugs.io.IO;
040:        import edu.umd.cs.findbugs.util.ClassName;
041:
042:        /**
043:         * Parse a class to extract symbolic information.
044:         * see <a href=http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html">
045:         http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html </a>
046:         * 
047:         * @author David Hovemeyer
048:         */
049:        public class ClassParser implements  ClassParserInterface {
050:
051:            static class Constant {
052:                int tag;
053:                Object[] data;
054:
055:                Constant(int tag, Object[] data) {
056:                    this .tag = tag;
057:                    this .data = data;
058:                }
059:            }
060:
061:            private DataInputStream in;
062:            private ClassDescriptor expectedClassDescriptor;
063:            private ICodeBaseEntry codeBaseEntry;
064:            private Constant[] constantPool;
065:            private ClassDescriptor immediateEnclosingClass;
066:
067:            /**
068:             * Constructor.
069:             * 
070:             * @param in                       the DataInputStream to read class data from
071:             * @param expectedClassDescriptor  ClassDescriptor expected: null if unknown
072:             * @param codeBaseEntry            codebase entry class is loaded from
073:             */
074:            public ClassParser(DataInputStream in, @CheckForNull
075:            ClassDescriptor expectedClassDescriptor,
076:                    ICodeBaseEntry codeBaseEntry) {
077:                this .in = in;
078:                this .expectedClassDescriptor = expectedClassDescriptor;
079:                this .codeBaseEntry = codeBaseEntry;
080:            }
081:
082:            /* (non-Javadoc)
083:             * @see edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder)
084:             */
085:            public void parse(ClassNameAndSuperclassInfo.Builder builder)
086:                    throws InvalidClassFileFormatException {
087:                try {
088:                    int magic = in.readInt();
089:                    if (magic != 0xcafebabe)
090:                        throw new InvalidClassFileFormatException(
091:                                "Classfile header isn't 0xCAFEBABE",
092:                                expectedClassDescriptor, codeBaseEntry);
093:                    int major_version = in.readUnsignedShort();
094:                    int minor_version = in.readUnsignedShort();
095:                    int constant_pool_count = in.readUnsignedShort();
096:
097:                    constantPool = new Constant[constant_pool_count];
098:                    for (int i = 1; i < constantPool.length; i++) {
099:                        constantPool[i] = readConstant();
100:                        if (constantPool[i].tag == IClassConstants.CONSTANT_Double
101:                                || constantPool[i].tag == IClassConstants.CONSTANT_Long) {
102:                            // Double and Long constants take up two constant pool entries
103:                            ++i;
104:                        }
105:                    }
106:
107:                    int access_flags = in.readUnsignedShort();
108:
109:                    int this _class = in.readUnsignedShort();
110:                    ClassDescriptor this ClassDescriptor = getClassDescriptor(this _class);
111:
112:                    int super _class = in.readUnsignedShort();
113:                    ClassDescriptor super ClassDescriptor = getClassDescriptor(super _class);
114:
115:                    int interfaces_count = in.readUnsignedShort();
116:                    if (interfaces_count < 0) {
117:                        throw new InvalidClassFileFormatException(
118:                                expectedClassDescriptor, codeBaseEntry);
119:                    }
120:                    ClassDescriptor[] interfaceDescriptorList = new ClassDescriptor[interfaces_count];
121:                    for (int i = 0; i < interfaceDescriptorList.length; i++) {
122:                        interfaceDescriptorList[i] = getClassDescriptor(in
123:                                .readUnsignedShort());
124:                    }
125:                    // Extract all references to other classes,
126:                    // both CONSTANT_Class entries and also referenced method
127:                    // signatures.
128:                    Collection<ClassDescriptor> referencedClassDescriptorList = extractReferencedClasses();
129:
130:                    builder.setClassDescriptor(this ClassDescriptor);
131:                    builder.setSuperclassDescriptor(super ClassDescriptor);
132:                    builder.setInterfaceDescriptorList(interfaceDescriptorList);
133:                    builder.setCodeBaseEntry(codeBaseEntry);
134:                    builder.setAccessFlags(access_flags);
135:                    builder
136:                            .setReferencedClassDescriptors(referencedClassDescriptorList);
137:                    builder.setClassfileVersion(major_version, minor_version);
138:                } catch (IOException e) {
139:                    throw new InvalidClassFileFormatException(
140:                            expectedClassDescriptor, codeBaseEntry, e);
141:                }
142:            }
143:
144:            /* (non-Javadoc)
145:             * @see edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd.cs.findbugs.classfile.analysis.ClassInfo.Builder)
146:             */
147:            public void parse(ClassInfo.Builder builder)
148:                    throws InvalidClassFileFormatException {
149:                throw new UnsupportedOperationException(
150:                        "Need to use a ClassParserUsingASM to build ClassInfo");
151:            }
152:
153:            /**
154:             * Extract references to other classes.
155:             * 
156:             * @return array of ClassDescriptors of referenced classes
157:             * @throws InvalidClassFileFormatException
158:             */
159:            private Collection<ClassDescriptor> extractReferencedClasses()
160:                    throws InvalidClassFileFormatException {
161:                Set<ClassDescriptor> referencedClassSet = new HashSet<ClassDescriptor>();
162:                for (Constant constant : constantPool) {
163:                    if (constant == null) {
164:                        continue;
165:                    }
166:                    if (constant.tag == IClassConstants.CONSTANT_Class) {
167:                        @SlashedClassName
168:                        String className = getUtf8String((Integer) constant.data[0]);
169:                        if (className.indexOf('[') >= 0) {
170:                            extractReferencedClassesFromSignature(
171:                                    referencedClassSet, className);
172:                        } else if (ClassName.isValidClassName(className)) {
173:                            referencedClassSet.add(DescriptorFactory.instance()
174:                                    .getClassDescriptor(className));
175:                        }
176:                    } else if (constant.tag == IClassConstants.CONSTANT_Methodref
177:                            || constant.tag == IClassConstants.CONSTANT_Fieldref
178:                            || constant.tag == IClassConstants.CONSTANT_InterfaceMethodref) {
179:                        // Get the target class name
180:                        String className = getClassName((Integer) constant.data[0]);
181:                        extractReferencedClassesFromSignature(
182:                                referencedClassSet, className);
183:
184:                        // Parse signature to extract class names
185:                        String signature = getSignatureFromNameAndType((Integer) constant.data[1]);
186:                        extractReferencedClassesFromSignature(
187:                                referencedClassSet, signature);
188:                    }
189:                }
190:                return referencedClassSet;
191:            }
192:
193:            /**
194:             * @param referencedClassSet
195:             * @param signature
196:             */
197:            public static void extractReferencedClassesFromSignature(
198:                    Set<ClassDescriptor> referencedClassSet, String signature) {
199:                while (signature.length() > 0) {
200:                    int start = signature.indexOf('L');
201:                    if (start < 0) {
202:                        break;
203:                    }
204:                    int end = signature.indexOf(';', start);
205:                    if (end < 0) {
206:                        break;
207:                    }
208:                    @SlashedClassName
209:                    String className = signature.substring(start + 1, end);
210:                    if (ClassName.isValidClassName(className)) {
211:                        referencedClassSet.add(DescriptorFactory.instance()
212:                                .getClassDescriptor(className));
213:                    }
214:                    signature = signature.substring(end + 1);
215:                }
216:            }
217:
218:            // 8: UTF-8 string
219:            // I: int
220:            // F: float
221:            // L: long
222:            // D: double
223:            // i: 2-byte constant pool index
224:            private static final String[] CONSTANT_FORMAT_MAP = { null, "8", // 1: CONSTANT_Utf8
225:                    null, "I", // 3: CONSTANT_Integer
226:                    "F", // 4: CONSTANT_Float
227:                    "L", // 5: CONSTANT_Long
228:                    "D", // 6: CONSTANT_Double
229:                    "i", // 7: CONSTANT_Class
230:                    "i", // 8: CONSTANT_String 
231:                    "ii", // 9: CONSTANT_Fieldref
232:                    "ii", // 10: CONSTANT_Methodref
233:                    "ii", // 11: CONSTANT_InterfaceMethodref
234:                    "ii", // 12: CONSTANT_NameAndType
235:            };
236:
237:            /**
238:             * Read a constant from the constant pool.
239:             * 
240:             * @return a Constant
241:             * @throws InvalidClassFileFormatException
242:             * @throws IOException 
243:             */
244:            private Constant readConstant()
245:                    throws InvalidClassFileFormatException, IOException {
246:                int tag = in.readUnsignedByte();
247:                if (tag < 0 || tag >= CONSTANT_FORMAT_MAP.length
248:                        || CONSTANT_FORMAT_MAP[tag] == null) {
249:                    throw new InvalidClassFileFormatException(
250:                            expectedClassDescriptor, codeBaseEntry);
251:                }
252:                String format = CONSTANT_FORMAT_MAP[tag];
253:                Object[] data = new Object[format.length()];
254:                for (int i = 0; i < format.length(); i++) {
255:                    char spec = format.charAt(i);
256:                    switch (spec) {
257:                    case '8':
258:                        data[i] = in.readUTF();
259:                        break;
260:                    case 'I':
261:                        data[i] = (Integer) in.readInt();
262:                        break;
263:                    case 'F':
264:                        data[i] = new Float(in.readFloat());
265:                        break;
266:                    case 'L':
267:                        data[i] = (Long) in.readLong();
268:                        break;
269:                    case 'D':
270:                        data[i] = new Double(in.readDouble());
271:                        break;
272:                    case 'i':
273:                        data[i] = (Integer) in.readUnsignedShort();
274:                        break;
275:                    default:
276:                        throw new IllegalStateException();
277:                    }
278:                }
279:
280:                return new Constant(tag, data);
281:            }
282:
283:            /**
284:             * Get a class name from a CONSTANT_Class.
285:             * Note that this may be an array (e.g., "[Ljava/lang/String;").
286:             * 
287:             * @param index index of the constant
288:             * @return the class name
289:             * @throws InvalidClassFileFormatException
290:             */
291:            private @SlashedClassName
292:            String getClassName(int index)
293:                    throws InvalidClassFileFormatException {
294:                if (index == 0) {
295:                    return null;
296:                }
297:
298:                checkConstantPoolIndex(index);
299:                Constant constant = constantPool[index];
300:                checkConstantTag(constant, IClassConstants.CONSTANT_Class);
301:
302:                int refIndex = ((Integer) constant.data[0]).intValue();
303:                String stringValue = getUtf8String(refIndex);
304:
305:                return stringValue;
306:            }
307:
308:            /**
309:             * Get the ClassDescriptor of a class referenced in the constant pool.
310:             * 
311:             * @param index        index of the referenced class in the constant pool
312:             * @return the ClassDescriptor of the referenced class
313:             * @throws InvalidClassFileFormatException 
314:             */
315:            private ClassDescriptor getClassDescriptor(int index)
316:                    throws InvalidClassFileFormatException {
317:                @SlashedClassName
318:                String className = getClassName(index);
319:                return className != null ? DescriptorFactory.instance()
320:                        .getClassDescriptor(className) : null;
321:            }
322:
323:            /**
324:             * Get the UTF-8 string constant at given constant pool index.
325:             * 
326:             * @param refIndex the constant pool index
327:             * @return the String at that index
328:             * @throws InvalidClassFileFormatException
329:             */
330:            private String getUtf8String(int refIndex)
331:                    throws InvalidClassFileFormatException {
332:                checkConstantPoolIndex(refIndex);
333:                Constant refConstant = constantPool[refIndex];
334:                checkConstantTag(refConstant, IClassConstants.CONSTANT_Utf8);
335:                return (String) refConstant.data[0];
336:            }
337:
338:            /**
339:             * Check that a constant pool index is valid.
340:             * 
341:             * @param expectedClassDescriptor   class descriptor
342:             * @param constantPool the constant pool
343:             * @param index        the index to check
344:             * @throws InvalidClassFileFormatException if the index is not valid
345:             */
346:            private void checkConstantPoolIndex(int index)
347:                    throws InvalidClassFileFormatException {
348:                if (index < 0 || index >= constantPool.length
349:                        || constantPool[index] == null) {
350:                    throw new InvalidClassFileFormatException(
351:                            expectedClassDescriptor, codeBaseEntry);
352:                }
353:            }
354:
355:            /**
356:             * Check that a constant has the expected tag.
357:             * 
358:             * @param constant     the constant to check
359:             * @param expectedTag the expected constant tag
360:             * @throws InvalidClassFileFormatException if the constant's tag does not match the expected tag
361:             */
362:            private void checkConstantTag(Constant constant, int expectedTag)
363:                    throws InvalidClassFileFormatException {
364:                if (constant.tag != expectedTag) {
365:                    throw new InvalidClassFileFormatException(
366:                            expectedClassDescriptor, codeBaseEntry);
367:                }
368:            }
369:
370:            interface FieldOrMethodDescriptorCreator<E> {
371:                public E create(String className, String name,
372:                        String signature, int accessFlags);
373:            }
374:
375:            /**
376:             * Read field_info, return FieldDescriptor.
377:             * 
378:             * @param thisClassDescriptor the ClassDescriptor of this class (being parsed) 
379:             * @return the FieldDescriptor
380:             * @throws IOException 
381:             * @throws InvalidClassFileFormatException 
382:             */
383:            private FieldDescriptor readField(
384:                    ClassDescriptor this ClassDescriptor) throws IOException,
385:                    InvalidClassFileFormatException {
386:                return readFieldOrMethod(this ClassDescriptor,
387:                        new FieldOrMethodDescriptorCreator<FieldDescriptor>() {
388:                            /* (non-Javadoc)
389:                             * @see edu.umd.cs.findbugs.classfile.engine.ClassParser.FieldOrMethodDescriptorCreator#create(java.lang.String, java.lang.String, java.lang.String, int)
390:                             */
391:                            public FieldDescriptor create(String className,
392:                                    String name, String signature,
393:                                    int accessFlags) {
394:                                return DescriptorFactory
395:                                        .instance()
396:                                        .getFieldDescriptor(
397:                                                className,
398:                                                name,
399:                                                signature,
400:                                                (accessFlags & IClassConstants.ACC_STATIC) != 0);
401:                            }
402:                        });
403:            }
404:
405:            /**
406:             * Read method_info, read method descriptor.
407:             * 
408:             * @param thisClassDescriptor
409:             * @return
410:             * @throws IOException 
411:             * @throws InvalidClassFileFormatException 
412:             */
413:            private MethodDescriptor readMethod(
414:                    ClassDescriptor this ClassDescriptor)
415:                    throws InvalidClassFileFormatException, IOException {
416:                return readFieldOrMethod(this ClassDescriptor,
417:                        new FieldOrMethodDescriptorCreator<MethodDescriptor>() {
418:                            /* (non-Javadoc)
419:                             * @see edu.umd.cs.findbugs.classfile.engine.ClassParser.FieldOrMethodDescriptorCreator#create(java.lang.String, java.lang.String, java.lang.String, int)
420:                             */
421:                            public MethodDescriptor create(String className,
422:                                    String name, String signature,
423:                                    int accessFlags) {
424:                                return DescriptorFactory
425:                                        .instance()
426:                                        .getMethodDescriptor(
427:                                                className,
428:                                                name,
429:                                                signature,
430:                                                (accessFlags & IClassConstants.ACC_STATIC) != 0);
431:                            }
432:                        });
433:            }
434:
435:            /**
436:             * Read field_info or method_info.
437:             * They have the same format.
438:             * 
439:             * @param <E>                 descriptor type to return
440:             * @param thisClassDescriptor class descriptor of class being parsed
441:             * @param creator             callback to create the FieldDescriptor or MethodDescriptor
442:             * @return the parsed descriptor
443:             * @throws IOException
444:             * @throws InvalidClassFileFormatException
445:             */
446:            private <E> E readFieldOrMethod(
447:                    ClassDescriptor this ClassDescriptor,
448:                    FieldOrMethodDescriptorCreator<E> creator)
449:                    throws IOException, InvalidClassFileFormatException {
450:                int access_flags = in.readUnsignedShort();
451:                int name_index = in.readUnsignedShort();
452:                int descriptor_index = in.readUnsignedShort();
453:                int attributes_count = in.readUnsignedShort();
454:
455:                String name = getUtf8String(name_index);
456:                String signature = getUtf8String(descriptor_index);
457:                if (attributes_count < 0) {
458:                    throw new InvalidClassFileFormatException(
459:                            expectedClassDescriptor, codeBaseEntry);
460:                }
461:                for (int i = 0; i < attributes_count; i++) {
462:                    readAttribute();
463:                }
464:
465:                return creator.create(this ClassDescriptor.getClassName(), name,
466:                        signature, access_flags);
467:            }
468:
469:            /**
470:             * Read an attribute.
471:             * 
472:             * @throws IOException 
473:             * @throws InvalidClassFileFormatException 
474:             */
475:            private void readAttribute() throws IOException,
476:                    InvalidClassFileFormatException {
477:                int attribute_name_index = in.readUnsignedShort();
478:                String attrName = getUtf8String(attribute_name_index);
479:
480:                int attribute_length = in.readInt();
481:                if (attribute_length < 0) {
482:                    throw new InvalidClassFileFormatException(
483:                            expectedClassDescriptor, codeBaseEntry);
484:                }
485:
486:                if (attrName.equals("InnerClasses")) {
487:                    readInnerClassesAttribute(attribute_length);
488:                } else {
489:                    IO.skipFully(in, attribute_length);
490:                }
491:            }
492:
493:            /**
494:             * Read an InnerClasses attribute.
495:             * 
496:             * @param attribute_length length of attribute (excluding first 6 bytes)
497:             * @throws InvalidClassFileFormatException
498:             * @throws IOException
499:             */
500:            private void readInnerClassesAttribute(int attribute_length)
501:                    throws InvalidClassFileFormatException, IOException {
502:                int number_of_classes = in.readUnsignedShort();
503:                if (attribute_length != number_of_classes * 8) {
504:                    throw new InvalidClassFileFormatException(
505:                            expectedClassDescriptor, codeBaseEntry);
506:                }
507:
508:                for (int i = 0; i < number_of_classes; i++) {
509:                    int inner_class_info_index = in.readUnsignedShort();
510:                    int outer_class_info_index = in.readUnsignedShort();
511:                    int inner_name_index = in.readUnsignedShort();
512:                    int inner_class_access_flags = in.readUnsignedShort();
513:
514:                    if (outer_class_info_index != 0) {
515:                        // Record which class this class is a member of.
516:                        this .immediateEnclosingClass = getClassDescriptor(outer_class_info_index);
517:                    }
518:                }
519:            }
520:
521:            /**
522:             * Get the signature from a CONSTANT_NameAndType.
523:             * 
524:             * @param index the index of the CONSTANT_NameAndType
525:             * @return the signature
526:             * @throws InvalidClassFileFormatException 
527:             */
528:            private String getSignatureFromNameAndType(int index)
529:                    throws InvalidClassFileFormatException {
530:                checkConstantPoolIndex(index);
531:                Constant constant = constantPool[index];
532:                checkConstantTag(constant, IClassConstants.CONSTANT_NameAndType);
533:                return getUtf8String((Integer) constant.data[1]);
534:            }
535:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.