001/*
002 * Copyright (c) 1998-2012 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: barseghian $'
006 * '$Date: 2012-04-27 13:16:27 -0700 (Fri, 27 Apr 2012) $' 
007 * '$Revision: 29789 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029package org.kepler.metadata;
030
031import java.io.IOException;
032import java.io.InputStreamReader;
033import java.io.Reader;
034import java.io.UnsupportedEncodingException;
035import java.util.ArrayList;
036import java.util.List;
037import java.util.Vector;
038
039import org.apache.commons.io.IOUtils;
040import org.apache.log4j.Logger;
041import org.ecoinformatics.seek.datasource.eml.eml2.Eml200Parser;
042import org.kepler.configuration.ConfigurationManager;
043import org.kepler.configuration.ConfigurationNamespace;
044import org.kepler.configuration.ConfigurationProperty;
045import org.kepler.objectmanager.data.db.Attribute;
046import org.kepler.objectmanager.data.db.Entity;
047import org.xml.sax.InputSource;
048
049import ptolemy.actor.TypedIOPort;
050import ptolemy.actor.lib.LimitedFiringSource;
051import ptolemy.data.ArrayToken;
052import ptolemy.data.OrderedRecordToken;
053import ptolemy.data.StringToken;
054import ptolemy.data.Token;
055import ptolemy.data.type.ArrayType;
056import ptolemy.data.type.BaseType;
057import ptolemy.kernel.CompositeEntity;
058import ptolemy.kernel.util.IllegalActionException;
059import ptolemy.kernel.util.NameDuplicationException;
060
061/**
062 * @author Derik Barseghian
063 * @version $Id: MetadataParser.java 29789 2012-04-27 20:16:27Z barseghian $
064 */
065public class MetadataParser extends LimitedFiringSource {
066
067        private static final Logger logger = Logger.getLogger(MetadataParser.class);
068        private static final String CONFIGURATIONNAMESPACE = "metadataParser";
069        private static final String MODULENAME = "actors";
070        private static final String MAPPINGLIST = "mappingList";
071        private static final String MAPPING = "mapping";
072        private static final String TYPE = "type";
073        private static final String CLASS = "class";
074        private TypedIOPort metadataInputPort;
075        private TypedIOPort metadataTypeInputPort;
076        private Vector<MetadataTypeParserMap> metadataTypeClassMapList = null;
077        
078        public MetadataParser(CompositeEntity container, String name)
079                        throws NameDuplicationException, IllegalActionException {
080                super(container, name);
081                
082                metadataInputPort = new TypedIOPort(this, "metadataInputPort", true, false);
083                metadataTypeInputPort = new TypedIOPort(this, "metadataTypeInputPort", true, false);
084
085    output.setTypeEquals(new ArrayType(BaseType.RECORD));
086    metadataTypeClassMapList = getMetadataTypeParserMap();
087        }
088        
089        public void fire() throws IllegalActionException {
090                super.fire();
091        
092                if (metadataInputPort.numberOfSources() <= 0){
093                        return;
094                }
095                if (metadataTypeInputPort.numberOfSources() <= 0){
096      return;
097    }
098                
099                StringToken metadataTypeStringToken = (StringToken) metadataTypeInputPort.get(0);
100                String metadataType = metadataTypeStringToken.stringValue();
101                
102                ParserInterface parser = createParser(metadataType);
103                if(parser == null) {
104                  throw new IllegalActionException("Kepler can't find a metadata parser for the type "+metadataType+". Please check the metadataParser.xml file.");
105                }
106    StringToken metadataStringToken = (StringToken) metadataInputPort.get(0);
107    
108    //logger.debug("received on input port:\n"+metadataStringToken.stringValue());
109        
110                try {
111                // FIXME character encoding
112                        InputStreamReader inputStreamReader = new InputStreamReader(IOUtils.toInputStream(metadataStringToken.stringValue(), "UTF-16"), "UTF-16");
113                        ArrayList<OrderedRecordToken> orderedRecordTokens = parsePackage(inputStreamReader, parser);
114                        if (output.numberOfSinks() > 0){
115                                OrderedRecordToken[] ors = orderedRecordTokens.toArray(new OrderedRecordToken[orderedRecordTokens.size()]);
116                                output.broadcast(new ArrayToken(ors));
117                                //for (int i=0; i<ors.length; i++){
118                                //      output.send(0, ors[0]);
119                                //}
120                        }
121                } catch (UnsupportedEncodingException e) {
122                        // TODO Auto-generated catch block
123                        e.printStackTrace();
124                } catch (IOException e) {
125                        // TODO Auto-generated catch block
126                        e.printStackTrace();
127                }
128        
129
130        }
131        
132        public void preinitialize() throws IllegalActionException {
133            super.preinitialize();
134        }
135        
136        /** The director told us to stop firing immediately. */
137        public void stop() {
138                super.stop();
139        }
140        
141        public void wrapup() throws IllegalActionException {
142                super.wrapup();
143        }
144        
145        
146        /*
147         *Create a parser object from the metadataParser.xml configuration according to the metadata type.
148         *The null will be returned if no parser class can be found. 
149         */
150        private ParserInterface createParser(String metadataType) {
151          ParserInterface parser = null;
152          if(metadataType != null ) {
153            if(metadataTypeClassMapList != null ) {
154              for(MetadataTypeParserMap map : metadataTypeClassMapList) {
155                if(map != null) {
156                  if(metadataType.equals(map.getMetadataType())) {
157                    String className = map.getClassName();
158                    if(className != null) {
159                      try {
160                        Class classDefinition = Class.forName(className);
161                        parser = (ParserInterface)classDefinition.newInstance();
162                        break;
163                      } catch (InstantiationException e) {
164                        logger.warn("MetadataPaser.createParser - can't get the parser object since "+e.getMessage());
165                        continue;
166                      } catch (IllegalAccessException e) {
167                        logger.warn("MetadataPaser.createParser - can't get the parser object since "+e.getMessage());
168                  continue;
169                      } catch (ClassNotFoundException e) {
170                        logger.warn("MetadataPaser.createParser - can't get the parser object since "+e.getMessage());
171                  continue;
172                      }
173                    }
174                  }
175                }
176              }
177            }
178          }
179          return parser;
180        }
181        
182        /*
183         * Get the the mapping between the parser class name and metadata type from the configuration file - metadataParser.xml
184         */
185        private Vector<MetadataTypeParserMap> getMetadataTypeParserMap() {
186          Vector<MetadataTypeParserMap> mapping = new Vector <MetadataTypeParserMap>();
187          ConfigurationManager manager = ConfigurationManager.getInstance();
188          if(manager != null) {
189            ConfigurationProperty cp = manager.getProperty(ConfigurationManager.getModule(MODULENAME),
190          new ConfigurationNamespace(CONFIGURATIONNAMESPACE));
191            if(cp != null) {
192              //System.out.println("==== the property file is not null");
193              ConfigurationProperty mappingListProperty = cp.getProperty(MAPPINGLIST);
194              if (mappingListProperty != null) {
195                //System.out.println("==== the mapping list  is not null");
196                List<ConfigurationProperty> mappingProperties = mappingListProperty.getProperties(MAPPING);
197                if(mappingProperties != null) {
198                  for(ConfigurationProperty mappingProperty : mappingProperties) {
199                    //System.out.println("==== the mapping  is not null");
200                    ConfigurationProperty metadataTypeProperty = mappingProperty.getProperty(TYPE);
201                    ConfigurationProperty classProperty = mappingProperty.getProperty(CLASS);
202                    if(metadataTypeProperty != null && classProperty != null ) {
203                      MetadataTypeParserMap map = new MetadataTypeParserMap();
204                      //System.out.println("==== add class name "+classProperty.getValue());
205                      //System.out.println("==== add metadata type "+metadataTypeProperty.getValue());
206                      map.setClassName(classProperty.getValue());
207                      map.setMetadataType(metadataTypeProperty.getValue());
208                      mapping.add(map);
209                    }
210                  }
211                  
212                }
213                
214              }
215              
216            }
217          }
218          return mapping;
219          
220        }
221
222
223        private ArrayList<OrderedRecordToken> parsePackage(Reader reader, ParserInterface parser) throws IllegalActionException {
224                Eml200Parser eml200Parser;
225
226                ArrayList<OrderedRecordToken> orderedRecordTokens = new ArrayList<OrderedRecordToken>();
227
228
229                try { // parse the package for the names and types of the atts
230                        //eml200Parser = new Eml200Parser();
231                        parser.parse(new InputSource(reader));
232                        //String namespace = eml200Parser.getNameSpace();
233                        
234                        logger.debug("entity count:"+parser.getEntityCount());
235                        
236                        //TODO check if eml parser be refactored to use generics / if this typing is safe:
237                        List<Entity> entities = parser.getEntities();
238                        if(entities != null ) {
239                          for (Entity entity: entities){
240                ArrayList<String> labels = new ArrayList<String>();
241                ArrayList<Token> tokens = new ArrayList<Token>();
242                logger.debug("name:" + entity.getName() + " getAttributes().length:" + entity.getAttributes().length);
243                //logger.debug("key:"+key + " name:" + entity.getName() + " num attributes:" + entity.getAttributes().length + " entity:\n"+entity.toXml());
244                
245                labels.add("entityName");
246                tokens.add(new StringToken(entity.getName()));
247                int numAttributes = entity.getAttributes().length;
248                
249                if (numAttributes > 0){
250                  labels.add("attributeName");
251                  labels.add("attributeDataType");
252                
253                  Token[] attributeNames = new Token[numAttributes];
254                  Token[] attributeDataTypes = new Token[numAttributes];
255                  
256                  for (int i=0; i< numAttributes; i++){
257                    Attribute a = entity.getAttributes()[i];
258                    logger.debug("attributeName:" + a.getName() +  
259                        " attributeDataType:" + a.getDataType());
260                        //" unit:" + a.getUnit() +
261                        //" unitType:" + a.getUnitType() + 
262                        //" measurementScale:" + a.getMeasurementScale());
263                    attributeNames[i] = new StringToken(a.getName());
264                    attributeDataTypes[i] = new StringToken(a.getDataType());
265
266                  }
267                  
268                  tokens.add(new ArrayToken(attributeNames));
269                  tokens.add(new ArrayToken(attributeDataTypes));
270                }
271                
272                String[] labelArray = labels.toArray(new String[labels.size()]);
273                Token[] tokenArray = tokens.toArray(new Token[tokens.size()]);
274                orderedRecordTokens.add(new OrderedRecordToken(labelArray, tokenArray));
275              }
276                        }
277                        
278                        
279                        return orderedRecordTokens;
280                        
281                } catch (Exception e) {
282                        e.printStackTrace();
283                        throw new IllegalActionException("Error parsing the eml package: "
284                                        + e.getMessage());
285                }
286
287        }
288        
289        /*
290         * A class represents the map between the metadata type and parser from the configuration file.
291         */
292        private class MetadataTypeParserMap {
293          private String metadataType = null;
294          private String className = null;
295          
296          /**
297           * Constructor
298           */
299          public MetadataTypeParserMap() {
300            
301          }
302          
303          /**
304           * Set the metadata type.
305           * @param metadataType - the type of the metadata.
306           */
307          public void setMetadataType(String metadataType) {
308            this.metadataType = metadataType;
309          }
310          
311          /**
312           * Get the metadata type. It can be null.
313           * @return the type of the metadata.
314           */
315          public String getMetadataType() {
316            return metadataType;
317          }
318          
319          /**
320           * Set the class name.
321           * @param className - the name of the class.
322           */
323          public void setClassName(String className) {
324            this.className = className;
325          }
326          
327          /**
328           * Get the class name mapping the metadata type. It can be null.
329           * @return the class name.
330           */
331          public String getClassName() {
332            return this.className;
333          }
334        }
335
336}