001/*
002 * Copyright (c) 2010-2011 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * 
006 * Permission is hereby granted, without written agreement and without
007 * license or royalty fees, to use, copy, modify, and distribute this
008 * software and its documentation for any purpose, provided that the above
009 * copyright notice and the following two paragraphs appear in all copies
010 * of this software.
011 *
012 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 * SUCH DAMAGE.
017 *
018 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 * ENHANCEMENTS, OR MODIFICATIONS.
024 *
025 */
026
027package org.kepler.kar.karxml;
028
029import java.io.ByteArrayInputStream;
030import java.io.File;
031import java.io.FileInputStream;
032import java.io.FileNotFoundException;
033import java.io.FileWriter;
034import java.io.IOException;
035import java.io.InputStream;
036import java.io.Reader;
037import java.io.StringWriter;
038import java.io.Writer;
039import java.util.ArrayList;
040import java.util.Arrays;
041import java.util.Collections;
042import java.util.List;
043
044import javax.xml.XMLConstants;
045import javax.xml.parsers.DocumentBuilder;
046import javax.xml.parsers.DocumentBuilderFactory;
047import javax.xml.parsers.ParserConfigurationException;
048import javax.xml.transform.OutputKeys;
049import javax.xml.transform.Source;
050import javax.xml.transform.Transformer;
051import javax.xml.transform.TransformerException;
052import javax.xml.transform.TransformerFactory;
053import javax.xml.transform.dom.DOMSource;
054import javax.xml.transform.stream.StreamResult;
055import javax.xml.transform.stream.StreamSource;
056import javax.xml.validation.Schema;
057import javax.xml.validation.SchemaFactory;
058import javax.xml.validation.Validator;
059import javax.xml.xpath.XPath;
060import javax.xml.xpath.XPathConstants;
061import javax.xml.xpath.XPathExpression;
062import javax.xml.xpath.XPathExpressionException;
063import javax.xml.xpath.XPathFactory;
064
065import org.apache.commons.logging.Log;
066import org.apache.commons.logging.LogFactory;
067import org.kepler.kar.KARFile;
068import org.kepler.moml.CompositeClassEntity;
069import org.w3c.dom.Document;
070import org.w3c.dom.Node;
071import org.w3c.dom.NodeList;
072import org.xml.sax.InputSource;
073import org.xml.sax.SAXException;
074
075import ptolemy.actor.TypedCompositeActor;
076
077/**
078 * Created by IntelliJ IDEA.
079 * User: sean
080 * Date: Feb 16, 2010
081 * Time: 1:53:33 PM
082 */
083public class KarXml {
084
085        private Document document;
086        private String name;
087        private long size;
088        private String karVersion;
089        private String manifestVersion;
090        private List<String> moduleDependencies;
091        private String lsid;
092        private List<KarEntry> karEntries;
093        
094        private boolean valid = false;
095        
096        
097        
098        public static final String COMPOSITEACTORTYPE = TypedCompositeActor.class.getName();
099        public static final String COMPOSITECLASSENTITYTYPE = CompositeClassEntity.class.getName();
100        private static final String DIRECTOR = "director";
101        public static final String REPORTLAYOUT = "ReportLayout";
102        private static XPathExpression GET_KAR_VERSION_EXPRESSION = null;
103        private static XPathExpression GET_NAME_EXPRESSION = null;
104        private static XPathExpression GET_SIZE_EXPRESSION = null;
105        private static XPathExpression GET_MANIFEST_VERSION_EXPRESSION = null;
106        private static XPathExpression GET_MODULE_DEPENDENCIES_EXPRESSION = null;
107        private static XPathExpression GET_LSID_EXPRESSION = null;
108        private static XPathExpression GET_KAR_ENTRIES = null;
109        private static XPathExpression GKE_NAME_EXPRESSION = null;
110        private static XPathExpression GKE_DEPENDS_ON_EXPRESSION = null;
111        private static XPathExpression GKE_TYPE_EXPRESSION = null;
112        private static XPathExpression GKE_LSID_EXPRESSION = null;
113        private static XPathExpression GKE_HANDLER_EXPRESSION = null;
114        //only used by 2.0.0:
115        private static XPathExpression GKE_DEPENDS_ON_MODULE_EXPRESSION = null;
116        private static XPathExpression GKE_XML_EXPRESSION = null;
117        private static XPathExpression GKE_GET_SEMANTIC_TYPES = null;
118        private static XPathExpression GKE_GET_WORKFLOW_NAME = null;
119        private static XPathExpression GKE_GET_DIRECTOR = null;
120        private static XPathExpression GKE_GET_DERIVEDFROM = null;
121        private String repositoryName;
122        private boolean hasReportLayoutInKarEntry = false;
123        
124        // accessors
125        public String getKarVersion() {
126                return karVersion;
127        }
128
129        public String getManifestVersion() {
130                return manifestVersion;
131        }
132
133        public List<String> getModuleDependencies() {
134                return moduleDependencies;
135        }
136
137        public String getLsid() {
138                return lsid;
139        }
140        
141
142        public Document getDocument() {
143                return document;
144        }
145
146        public List<KarEntry> getKarEntries() {
147                return karEntries;
148        }
149        // accessors end        
150        
151        private KarXml() {
152                if (GET_KAR_VERSION_EXPRESSION == null) {
153                        setupXPathExpressions();
154                }
155        }
156
157        private static void setupXPathExpressions() {
158                XPathFactory factory = XPathFactory.newInstance();
159                XPath xpath = factory.newXPath();
160                try {
161                        GET_KAR_VERSION_EXPRESSION = xpath.compile("/*[local-name() = 'kar']/mainAttributes/KAR-Version/text()");
162                        GET_NAME_EXPRESSION = xpath.compile("/*[local-name() = 'kar']/karFileName/text()");
163                        GET_SIZE_EXPRESSION = xpath.compile("/*[local-name() = 'kar']/karFileSize/text()");
164                        GET_MANIFEST_VERSION_EXPRESSION = xpath.compile("/*[local-name() = 'kar']/mainAttributes/Manifest-Version/text()");
165                        GET_MODULE_DEPENDENCIES_EXPRESSION = xpath.compile("/*[local-name() = 'kar']/mainAttributes/module-dependencies/text()");
166                        GET_LSID_EXPRESSION = xpath.compile("/*[local-name() = 'kar']/mainAttributes/lsid/text()");
167                        
168                        GET_KAR_ENTRIES = xpath.compile("/*[local-name() = 'kar']/karEntry");
169                        GKE_NAME_EXPRESSION = xpath.compile("karEntryAttributes/Name/text()");
170                        GKE_DEPENDS_ON_EXPRESSION = xpath.compile("karEntryAttributes/dependsOn/text()");
171                        GKE_TYPE_EXPRESSION = xpath.compile("karEntryAttributes/type/text()");
172                        GKE_LSID_EXPRESSION = xpath.compile("karEntryAttributes/lsid/text()");
173                        GKE_HANDLER_EXPRESSION = xpath.compile("karEntryAttributes/handler/text()");
174                        //only used by 2.0.0:
175                        GKE_DEPENDS_ON_MODULE_EXPRESSION = xpath.compile("karEntryAttributes/dependsOnModule/text()");
176                        GKE_XML_EXPRESSION = xpath.compile("karEntryXML");
177                        GKE_GET_SEMANTIC_TYPES = xpath.compile("karEntryXML/entity/property[@class=\"org.kepler.sms.SemanticType\"]/@value");
178                        GKE_GET_WORKFLOW_NAME = xpath.compile("karEntryXML/entity/@name");
179                        GKE_GET_DIRECTOR = xpath.compile("karEntryXML/entity/property/property[@name=\"entityId\"]/@value");
180                        GKE_GET_DERIVEDFROM =xpath.compile("karEntryXML/entity/property/property[@name=\"derivedFrom\"]/@value");
181                } catch(XPathExpressionException ex) {
182                        log.error("Exception", ex);
183                        // Make sure all expression are in a mutually consistent state
184                        GET_KAR_VERSION_EXPRESSION = null;
185                        GET_NAME_EXPRESSION = null;
186                        GET_SIZE_EXPRESSION = null;
187                        GET_MANIFEST_VERSION_EXPRESSION = null;
188                        GET_MODULE_DEPENDENCIES_EXPRESSION = null;
189                        GET_LSID_EXPRESSION = null;
190
191                        GET_KAR_ENTRIES = null;
192                        GKE_DEPENDS_ON_EXPRESSION = null;
193                        GKE_TYPE_EXPRESSION = null;
194                        GKE_LSID_EXPRESSION = null;
195                        GKE_HANDLER_EXPRESSION = null;
196                        //only used by 2.0.0:
197                        GKE_DEPENDS_ON_MODULE_EXPRESSION = null;
198                        GKE_XML_EXPRESSION = null;
199                        GKE_GET_SEMANTIC_TYPES = null;
200                        GKE_GET_WORKFLOW_NAME = null;
201                        GKE_GET_DIRECTOR = null;
202                        GKE_GET_DERIVEDFROM = null;
203                }
204        }
205
206        public static KarXml of(File file) {
207                try {
208                        return of(new FileInputStream(file));
209                }
210                catch(FileNotFoundException ex) {
211                        return null;
212                }
213        }
214        public static KarXml of(InputStream is) {
215                Document document = parseXmlStream(is);
216                if (document == null) {
217                        return null;
218                }
219                // Validate document
220                boolean valid = validateDocument(document);
221                if (!valid) {
222                        System.out.println("Invalid document.");
223                        return null;
224                }
225                KarXml kx = new KarXml();
226                kx.document = document;
227                kx.parse();
228                kx.fixSemanticTypes();
229                return kx;
230        }
231
232        private void fixSemanticTypes() {
233                
234                List<String> semanticTypes = null;
235                
236                // Go through each of the KAR XML entries in this object
237                
238                // Find a TypedCompositeActor with at least one semantic type on it
239                for (KarEntry entry : this.getKarEntries()) {
240                        if (entry.getType().endsWith(".TypedCompositeActor") && !entry.getSemanticTypes().isEmpty()) {
241                                semanticTypes = entry.getSemanticTypes();
242                                break;
243                        }
244                }
245                if (semanticTypes == null) {
246                        // Nothing has a semantic type. So nothing is going to be visible, sorry.
247                        log.warn("This KAR has no semantically-tagged composites, cannot determine semantic types to assign");
248                        return;
249                }
250                
251                // Assign that/those semantic type(s) to all entries without a semantic type already
252                for (KarEntry entry : this.getKarEntries()) {
253                        if (entry.getSemanticTypes().isEmpty()) {
254                                entry.semanticTypes = new ArrayList<String>(semanticTypes);
255                        }
256                }
257        }
258
259        
260        /**
261         * Validate the document against the schema. Currently we only validate against
262         * kar xml 2.0.0 and 2.1.0. If it is not a kar xml 2.0.0 or 2.1.0 xml, this method will return true.
263         * @param document  the document need to be validate
264         * @return true if it is a valid document
265         */
266        public static boolean validateDocument(Document document) {
267          if(document == null){
268            return false;
269          }
270                try {
271                  Node docElement = document.getDocumentElement();
272                  String nameSpace = docElement.getNamespaceURI();
273                  log.debug("The name space is ===== "+nameSpace);
274                  
275                  if(nameSpace == null  || !nameSpace.equals(KARFile.KAR_VERSION_200_NAMESPACE_DEFAULT)|| 
276                                  !nameSpace.equals(KARFile.KAR_VERSION_210_NAMESPACE_DEFAULT)){
277                    return true;
278                  }
279                  SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
280                  String resourceDir = KARFile.getResourceDir(nameSpace);
281                  String resourceFileName = KARFile.getResourceFileName(nameSpace);
282                  // ClassLoader.getResource javadoc says: 
283                  // The name of a resource is a '/'-separated path name that identifies the resource.
284                  // so I am using a hardcode / in this path:
285                  Source schemaFile = new StreamSource(KarXml.class.getClassLoader().getResourceAsStream(resourceDir +
286                                  "/" + resourceFileName));
287                  Schema schema = factory.newSchema(schemaFile);
288                  Validator validator = schema.newValidator();
289                  validator.validate(new DOMSource(document));
290                }
291                catch(SAXException ex) {
292                        ex.printStackTrace();
293                        return false;
294                }
295                catch(IOException ex) {
296                        ex.printStackTrace();
297                        return false;
298                }
299                log.debug("return true");
300                return true;
301        }
302
303        private void parse() {
304                try {
305                        NodeList nodes = (NodeList) GET_KAR_VERSION_EXPRESSION.evaluate(document, XPathConstants.NODESET);
306                        if (nodes.getLength() == 0) {
307                                log.warn("No KAR version found");
308                        }
309                        else {
310                                valid = true;   // NOTE: At present, this is what is used to
311                                                                // determine if the file is a valid KAR XML
312                                                                // file.
313                                karVersion = nodes.item(0).getNodeValue().trim();
314                        }
315                        
316                        nodes = (NodeList) GET_NAME_EXPRESSION.evaluate(document, XPathConstants.NODESET);
317                        if (nodes.getLength() == 0) {
318                                log.warn("No KAR name found");
319                        }
320                        else {
321                                name = nodes.item(0).getNodeValue().trim();
322                        }
323                        
324      //System.out.println("The kar name is ================ "+name);
325                        
326                        nodes = (NodeList) GET_SIZE_EXPRESSION.evaluate(document, XPathConstants.NODESET);
327                        if (nodes.getLength() == 0) {
328                                log.warn("No KAR size found");
329                                size = -1;
330                        }
331                        else {
332                                size = Long.valueOf(nodes.item(0).getNodeValue().trim());
333                        }
334                        
335                        nodes = (NodeList) GET_MANIFEST_VERSION_EXPRESSION.evaluate(document, XPathConstants.NODESET);
336                        if (nodes.getLength() == 0) {
337                                log.warn("No manifest version found");
338                        }
339                        else {
340                                manifestVersion = nodes.item(0).getNodeValue().trim();
341                        }
342                        
343                        nodes = (NodeList) GET_LSID_EXPRESSION.evaluate(document, XPathConstants.NODESET);
344                        if (nodes.getLength() == 0) {
345                                log.warn("No LSID found");
346                        }
347                        else {
348                                lsid = nodes.item(0).getNodeValue().trim();
349                        }
350                        //System.out.println("The kar file lisd is ============== "+lsid);
351                        
352                        nodes = (NodeList) GET_MODULE_DEPENDENCIES_EXPRESSION.evaluate(document, XPathConstants.NODESET);
353                        if (nodes.getLength() == 0) {
354                                log.warn("No module dependencies found");
355                        }
356                        else {
357                                String moduleDependenciesString = nodes.item(0).getNodeValue().trim();
358                                String[] dependencies = moduleDependenciesString.split(";");
359                                moduleDependencies = new ArrayList<String>(Arrays.asList((String[]) dependencies));
360                        }
361                        
362                        nodes = (NodeList) GET_KAR_ENTRIES.evaluate(document, XPathConstants.NODESET);
363                        if (nodes.getLength() == 0) {
364                                log.warn("No KAR entries found");
365                        }
366                        else {
367                                List<KarEntry> entries = new ArrayList<KarEntry>();
368                                for (int i = 0; i < nodes.getLength(); i++) {
369                                        Node node = nodes.item(i);
370                                        KarEntry entry = KarEntry.of(node, karVersion);
371                                        //System.out.println("kar entry name ==================="+entry.getName());
372                                        entry.setParent(this);
373                                        entries.add(entry);
374                                        if(entry.hasReportingLayout())
375                                        {
376                                          hasReportLayoutInKarEntry = true;
377                                        }
378                                }
379                                karEntries = entries;
380                        }
381                }
382                catch(XPathExpressionException ex) {
383                        log.error("Exception", ex);
384                }
385                
386                // Get the karEntry nodes
387                
388        }
389
390        public static Document parseXmlStream(InputStream is) {
391                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
392                factory.setNamespaceAware(true);
393                try {
394                        DocumentBuilder builder = factory.newDocumentBuilder();
395                        return builder.parse(is);
396                        // TODO: Have the document parser validate against the KAR XML DTD
397                        // (in progress)
398                }
399                catch(ParserConfigurationException ex) {
400                        log.error("Exception", ex);
401                }
402                catch(SAXException ex) {
403                        log.error("Exception", ex);
404                }
405                catch(IOException ex) {
406                        log.error("Exception", ex);
407                }
408                return null;
409        }
410        
411        public static Document parseXml(Reader reader) {
412                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
413                factory.setNamespaceAware(true);
414                try {
415                        InputSource source = new InputSource(reader);
416                        DocumentBuilder builder = factory.newDocumentBuilder();
417                        return builder.parse(source);
418                        // TODO: Have the document parser validate against the KAR XML DTD
419                        // (in progress)
420                }
421                catch(ParserConfigurationException ex) {
422                        log.error("Exception", ex);
423                }
424                catch(SAXException ex) {
425                        log.error("Exception", ex);
426                }
427                catch(IOException ex) {
428                        log.error("Exception", ex);
429                }
430                return null;
431        }
432        
433        // toString
434        @Override
435        public String toString() {
436                return "KarXml{" +
437                                "karVersion='" + getKarVersion() + '\'' +
438                                ", manifestVersion='" + getManifestVersion() + '\'' +
439                                ", moduleDependencies=" + getModuleDependencies() +
440                                ", lsid='" + getLsid() + '\'' +
441                                '}';
442        }
443
444        public boolean isValid() {
445                return valid;
446        }
447        
448        public String getId() {
449                return this.getLsid();
450        }
451        
452        public String getName() {
453                if (name == null) {
454                        // On some searches, this really floods stdout.
455//                      log.warn("No explicit name, using LSID instead");
456                        return this.getId();
457                }
458                else {
459                        return name;
460                }
461        }
462
463        /**
464         * @return The size of the KAR file in bytes, if available. -1, if not available.
465         */
466        public long getSize() {
467                return size;
468        }
469
470        public String getRepositoryName() {
471                return repositoryName;
472        }
473
474        public void setRepositoryName(String repositoryName) {
475                this.repositoryName = repositoryName;
476        }
477        
478        public boolean hasReportLayout()
479        {
480          return hasReportLayoutInKarEntry;
481        }
482
483        public static class KarEntry {
484                // disallow the default constructor
485                private KarEntry() {}
486                
487                private KarEntry(Node karEntryNode, String karVersion) {
488                        this();
489                        try {
490                                NodeList nodes = (NodeList) GKE_DEPENDS_ON_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
491                                if (nodes.getLength() == 0) {
492                                        log.warn("No dependsOn found");
493                                }
494                                else {
495                                        dependsOn = nodes.item(0).getNodeValue().trim();
496                                }
497                                
498                                nodes = (NodeList) GKE_NAME_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
499                                if (nodes.getLength() == 0) {
500                                        log.warn("No name found");
501                                }
502                                else {
503                                        name = nodes.item(0).getNodeValue().trim();
504                                }
505                                
506                                nodes = (NodeList) GKE_TYPE_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
507                                if (nodes.getLength() == 0) {
508                                        log.warn("No type found");
509                                }
510                                else {
511                                        type = nodes.item(0).getNodeValue().trim();
512                                        if(type != null)
513                                        {
514                                          type = type.trim();
515                                          //System.out.println("type is ==================== "+type);
516                                          if(type.equals(COMPOSITEACTORTYPE) || type.equals(COMPOSITECLASSENTITYTYPE))
517                                          {
518                                            //System.out.println("================it is a composite actor");
519                                            isCompositeActor = true;
520                                            //System.out.println("is a compoiste Actor ================ ");
521                                          }
522                                          else if(type.contains(REPORTLAYOUT))
523                                          {
524                                            hasReportingLayout = true;
525                                          }
526                                        }
527                                }
528                                
529                                nodes = (NodeList) GKE_LSID_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
530                                if (nodes.getLength() == 0) {
531                                        log.warn("No LSID found");
532                                }
533                                else {
534                                        lsid = nodes.item(0).getNodeValue().trim();
535                                }
536                                
537                                nodes = (NodeList) GKE_HANDLER_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
538                                if (nodes.getLength() == 0) {
539                                        log.warn("No handler found");
540                                }
541                                else {
542                                        handler = nodes.item(0).getNodeValue().trim();
543                                }
544                                
545                                //not used in 2.1:
546                                if (!karVersion.equals(KARFile.VERSION_2_1)){
547                                        nodes = (NodeList) GKE_DEPENDS_ON_MODULE_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
548                                        if (nodes.getLength() == 0) {
549                                                log.warn("No dependsOnModule found");
550                                        }
551                                        else {
552                                                dependsOnModule = nodes.item(0).getNodeValue().trim();
553                                        }
554                                }
555                                
556                                // if this composite actor type, we need to figure out if it has director
557                                if(isCompositeActor)
558                                {
559                                  nodes = (NodeList) GKE_GET_DIRECTOR.evaluate(karEntryNode, XPathConstants.NODESET);
560                                  if(nodes.getLength() >0)
561                                  {
562                                   
563                                    if(hasDirector(nodes))
564                                    {
565                                      //System.out.println("find a directory, this is workflow =============");
566                    hasDirector = true;
567                    //System.out.println("has director========");
568                    isWorkflow = true;
569                                    }
570                                    else
571                                    {
572                                      //check derivedForm
573                                      if(isDerivedFromDirector(karEntryNode))
574                                      {
575                                        hasDirector = true;
576                      //System.out.println("has derived director========");
577                      isWorkflow = true;
578                                      }
579                                    }
580                                   
581                                  }
582                                }
583                                //if this workflow, we need to get workflow name.
584                                //(getName method will get the name of the workflow xml file.)
585                                if(isWorkflow)
586                                {
587                                  nodes = (NodeList)GKE_GET_WORKFLOW_NAME.evaluate(karEntryNode, XPathConstants.NODESET);
588                                  if(nodes.getLength() == 0)
589                                  {
590                                    //System.out.println("Couldn't find workflow name, use the name of kar entry to replace it");
591                                    workflowName = name;
592                                  }
593                                  else
594                                  {
595                                    
596                                    workflowName = nodes.item(0).getNodeValue().trim();
597                                    //System.out.println("get the workflow name "+workflowName);
598                                  }
599                                }
600                                
601                                
602                                nodes = (NodeList) GKE_XML_EXPRESSION.evaluate(karEntryNode, XPathConstants.NODESET);
603                                if (nodes.getLength() == 0) {
604                                        log.warn("No XML found");
605                                }
606                                else {
607                                        xml = nodes.item(0);
608                                }
609                                
610                                nodes = (NodeList) GKE_GET_SEMANTIC_TYPES.evaluate(karEntryNode, XPathConstants.NODESET);
611                                if (nodes.getLength() == 0) {
612                                        log.warn("No semantic types found");
613                                }
614                                else {
615                                        List<String> semanticTypes = new ArrayList<String>();
616                                        for (int i = 0; i < nodes.getLength(); i++) {
617                                                Node node = nodes.item(i);
618                                                semanticTypes.add(node.getNodeValue());
619                                        }
620                                        this.semanticTypes = semanticTypes;
621                                }
622                        }
623                        catch(XPathExpressionException ex) {
624                                log.warn("Exception", ex);
625                        }                       
626                }
627                
628                
629                /*
630                 * Determine if the entity has a director
631                 */
632                private boolean hasDirector(NodeList directorNodeList)
633                {
634                  boolean hasDirector = false;
635                  if(directorNodeList != null)
636                  {
637                    for(int i=0 ; i<directorNodeList.getLength(); i++)
638                    {
639                      Node node = directorNodeList.item(i);
640                      String value = node.getNodeValue().trim();
641                      if(value != null && value.indexOf(DIRECTOR) != -1)
642                      {
643                        hasDirector = true;
644                        break;
645                      }
646                    }
647                  }
648                  return hasDirector;
649                  
650                }
651                
652                
653                /*
654                 * Determine if the entity is derived from a director
655                 */
656                private boolean isDerivedFromDirector(Node karEntryNode){
657                  boolean fromDirector = false;
658                  if(karEntryNode != null){
659                    try{
660                      NodeList nodeList = (NodeList)GKE_GET_DERIVEDFROM.evaluate(karEntryNode, XPathConstants.NODESET);
661                      if(nodeList != null){
662                        for(int i=0; i<nodeList.getLength(); i++){
663                          Node node = nodeList.item(i);
664                          if(node != null){
665                            String value = node.getNodeValue();
666                            if(value != null && value.contains(DIRECTOR)){
667                              fromDirector = true;
668                              break;
669                            }
670                          }
671                        }
672                      }
673                    }
674                    catch(Exception e){
675                      log.warn("Exception", e);
676                    }
677                  }
678                  return fromDirector;
679                }
680                
681                public String getName() {
682                        return name;
683                }
684                
685                public List<String> getSemanticTypes() {
686                        return semanticTypes == null ? getDefaultSemanticTypes() : semanticTypes;
687                }
688                
689                private List<String> getDefaultSemanticTypes() {
690                        return Collections.emptyList();
691                }
692
693                public String getDependsOn() {
694                        return dependsOn;
695                }
696
697                public String getType() {
698                        return type;
699                }
700
701                public String getLsid() {
702                        return lsid;
703                }
704
705                public String getHandler() {
706                        return handler;
707                }
708
709                //only used by 2.0.0:
710                public String getDependsOnModule() {
711                        return dependsOnModule;
712                }
713
714                public Node getXml() {
715                        return xml;
716                }
717                
718                public String getWorkflowName(){
719                  return workflowName;
720                }
721                
722                public boolean isWorkflow(){
723                  return isWorkflow;
724                }
725                
726                public boolean hasReportingLayout(){
727                  return hasReportingLayout;
728                }
729                
730                private String _asString() {
731                        return nodeToString(xml);
732                }
733                
734                
735                // Used for debugging
736                @SuppressWarnings({"UnusedDeclaration"})
737                public File asLocalFile() {
738                        Writer writer = null;
739                        String data = this._asString();
740                        File file = null;
741                        try {
742                                file = File.createTempFile("dump", ".xml");
743                                writer = new FileWriter(file);
744                                writer.write(data);
745                                System.out.println("Wrote fragment: " + file.getAbsolutePath());
746                        }
747                        catch(IOException ex) {
748                                System.out.println("Error dumping fragment");
749                        }
750                        finally {
751                                if (writer != null) {
752                                        try {
753                                                writer.close();
754                                        }
755                                        catch(IOException ignored) {}
756                                }
757                        }
758                        
759                        return file;
760                }
761                
762                public InputStream asInputStream() {
763                        return new ByteArrayInputStream(this._asString().getBytes());
764                }
765
766                // toString
767                @Override
768                public String toString() {
769                        
770                        String KARVersion = getParent().getKarVersion();
771                        
772                        if (!KARVersion.equals(KARFile.VERSION_2_1))
773                        {
774                                return "KarEntry{" +
775                                        "name='" + getName() + '\'' +
776                                        ", dependsOn='" + getDependsOn() + '\'' +
777                                        ", type='" + getType() + '\'' +
778                                        ", lsid='" + getLsid() + '\'' +
779                                        ", handler='" + getHandler() + '\'' +
780                                        ", dependsOnModule='" + getDependsOnModule() + '\'' +
781                                        ", xml=" + (getXml() == null ? "null" : "non-null") +
782                                        '}';
783                        }
784                        else{
785                                return "KarEntry{" +
786                                        "name='" + getName() + '\'' +
787                                        ", dependsOn='" + getDependsOn() + '\'' +
788                                        ", type='" + getType() + '\'' +
789                                        ", lsid='" + getLsid() + '\'' +
790                                        ", handler='" + getHandler() + '\'' +
791                                        //", dependsOnModule='" + getDependsOnModule() + '\'' +
792                                        ", xml=" + (getXml() == null ? "null" : "non-null") +
793                                        '}';
794                        }
795                }
796
797                public static KarEntry of(Node karEntryNode, String karVersion) {
798                        return new KarEntry(karEntryNode, karVersion);
799                }
800                
801                private String name = null;
802                private List<String> semanticTypes = null;
803                private String dependsOn = null;
804                private String type = null;
805                private String lsid = null;
806                private String handler = null;
807                //only used by 2.0.0:
808                private String dependsOnModule = null;
809                private Node xml = null;
810                private KarXml parent;
811                private boolean isCompositeActor = false;
812                private boolean hasReportingLayout = false;
813                private boolean hasDirector = false;
814                private boolean isWorkflow = false;
815                private String workflowName = null;
816
817                public void setParent(KarXml parent) {
818                        this.parent = parent;
819                }
820                
821                public KarXml getParent() {
822                        return parent;
823                }
824        }
825
826        private static final Log log = LogFactory.getLog(KarXml.class);
827        
828        public static String nodeToString(Node node) {
829                StringWriter sw = new StringWriter();
830                try {
831                        Transformer t = TransformerFactory.newInstance().newTransformer();
832                        t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
833                        NodeList nodes = node.getChildNodes();
834                        for (int i = 0; i < nodes.getLength(); i++) {
835                                t.transform(new DOMSource(nodes.item(i)), new StreamResult(sw));
836                        }
837                }
838                catch(TransformerException ex) {
839                        System.out.println("Transformer exception");
840                }
841                return sw.toString();
842        }
843}