001/*
002 *  $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/MusicBrainzRestRequester.java $
003 *  $Author: tgutwin $
004 *  $Revision: 1219 $
005 *  $Date: 2018-03-02 22:12:45 -0800 (Fri, 02 Mar 2018) $
006 */
007/*
008 *
009 *  Written by Tom Gutwin - WebARTS Design.
010 *  Copyright (C) 2014-2016 WebARTS Design, North Vancouver Canada
011 *  http://www.webarts.ca
012 *
013 *  This program is free software; you can redistribute it and/or modify
014 *  it under the terms of the GNU General Public License as published by
015 *  the Free Software Foundation; version 3 of the License, or
016 *  (at your option) any later version.
017 *
018 *  This program is distributed in the hope that it will be useful,
019 *  but WITHOUT ANY WARRANTY; without_ even the implied warranty of
020 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
021 *  GNU General Public License for more details.
022 *
023 *  You should have received a copy of the GNU General Public License
024 *  along with this program; if not, write to the Free Software
025 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
026 */
027
028package ca.bc.webarts.tools;
029
030import java.io.IOException;
031import java.io.File;
032import java.io.FileNotFoundException;
033import java.lang.Integer;
034import java.net.HttpURLConnection;
035import java.net.MalformedURLException;
036import java.net.URL;
037import java.net.URLEncoder;
038import java.util.Arrays;
039import java.util.Comparator;
040import java.util.Hashtable;
041import java.util.Set;
042import java.util.Vector;
043
044
045import ca.bc.webarts.tools.RestRequester;
046import ca.bc.webarts.widgets.Util;
047import ca.bc.webarts.widgets.Quick;
048
049import org.apache.commons.codec.binary.Base64;
050
051import nu.xom.Attribute;
052import nu.xom.Builder;
053import nu.xom.Document;
054import nu.xom.Element;
055import nu.xom.Elements;
056import nu.xom.Node;
057import nu.xom.ParsingException;
058import nu.xom.ValidityException;
059import nu.xom.Serializer;
060import nu.xom.XPathException;
061
062
063/**
064  * This class wraps the communication to the REST interface to MusicBrainz.
065  * The web service root URL is https://musicbrainz.org/ws/2/
066  * We have 12 resources on our web service which represent core entities in our database:
067  *  area, artist, event, instrument, label, place, recording, release, release-group, series, work, url
068  * We also provide a web service interface for the following non-core resources:
069  *  rating, tag, collection
070  * And we allow you to perform lookups based on other unique identifiers with these resources:
071  *  <code>discid, isrc, iswc</code><ul>
072  * On each entity resource, you can perform three different GET requests:
073  *
074  *  <li>lookup:   /&lt;ENTITY&gt;/&lt;MBID&gt;?inc=&lt;INC&gt;</li>
075  *  <li>browse:   /&lt;ENTITY&gt;?&lt;ENTITY&gt;=&lt;MBID&gt;&limit=&lt;LIMIT&gt;&offset=&lt;OFFSET&gt;&inc=&lt;INC&gt;</li>
076  *  <li>search:   /&lt;ENTITY&gt;?query=&lt;QUERY&gt;&limit=&lt;LIMIT&gt;&offset=&lt;OFFSET&gt;</li>
077  * ... except that search is not implemented for URL entities at this time.</ul><br />
078  *
079  *  Written by Tom Gutwin - WebARTS Design.<br />
080  *  Copyright &copy; 2018 WebARTS Design, North Vancouver Canada<br />
081  *  <a href="http://www.webarts.ca">http://www.webarts.ca</a>
082  *
083  * @author  Tom B. Gutwin
084  **/
085public class MusicBrainzRestRequester extends RestRequester
086{
087  protected static final String CLASSNAME = "ca.bc.webarts.tools.MusicBrainzRestRequester"; //ca.bc.webarts.widgets.Util.getCurrentClassName();
088  public static final String LOG_TAG = "\n"+CLASSNAME; //+"."+ca.bc.webarts.android.Util.getCurrentClassName();
089
090  /** DEFAULT MUSICBRAINZ IP address to use: 10.0.0.207 .**/
091  protected static final String DEFAULT_MUSICBRAINZ_IP = "musicbrainz.org";
092  /** DEFAULT MUSICBRAINZ username to use: admin .**/
093  protected static final String DEFAULT_MUSICBRAINZ_USERNAME = "admin";
094  /** DEFAULT MUSICBRAINZ password to use: admin .**/
095  protected static final String DEFAULT_MUSICBRAINZ_PASSWORD = "admin";
096  /** DEFAULT MUSICBRAINZ rest URL to start the URL path: /rest .**/
097  protected static final String DEFAULT_MUSICBRAINZ_REST_URL_PATHSTR = "/ws/2";
098  /** A default Release MRID to use for testing. Barenaked Ladies, Gordon. **/
099  protected static final String DEFAULT_MUSICBRAINZ_TEST_RELEASE_MRID = "38e73fee-7420-4f3a-805a-6fdfac011ac2";
100  protected static final String DEFAULT_MUSICBRAINZ_TEST_ARTIST_MRID = "86e736b4-93e2-40ff-9e1c-fb7c63fef5f6";
101
102
103  protected static StringBuilder helpMsg_ = new StringBuilder(SYSTEM_LINE_SEPERATOR);
104  protected static boolean debugOut_ = false;
105  protected static boolean doWrites_ = true;
106
107  protected String metaDataFilename_ = "mbData.props";
108
109  /** The start path to use in therest URL. Over-ride this if you extend this class. **/
110  protected String restUrlPath_ = DEFAULT_MUSICBRAINZ_REST_URL_PATHSTR;
111  Builder xmlBuilder_ = new Builder();
112
113  /**
114    * Default constructor .
115    *
116    * @see #DEFAULT_MUSICBRAINZ_IP
117    * @see #DEFAULT_MUSICBRAINZ_USERNAME
118    * @see #DEFAULT_MUSICBRAINZ_PASSWORD
119    **/
120  public MusicBrainzRestRequester()
121  {
122    authenticating_=false;
123    setBaseUrl( "https://"+DEFAULT_MUSICBRAINZ_IP+restUrlPath_);
124    setUsername( DEFAULT_MUSICBRAINZ_USERNAME);
125    setPassword( DEFAULT_MUSICBRAINZ_PASSWORD);
126  }
127
128
129  /**
130    * Constructor to customize all connection settings.
131    *
132    **/
133  public MusicBrainzRestRequester(String server, String user, String pass)
134  {
135    setBaseUrl( "https://"+server+restUrlPath_);
136    authenticating_=true;
137    setUsername(user);
138    setPassword( pass);
139  }
140
141  /**
142    * Set Method for class field 'restUrlPath_'.
143    *
144    * @param restUrlPath_ is the value to set this class field to.
145    *
146    **/
147  public  void setRestUrlPath(String restUrlPath)
148  {
149    restUrlPath_ = restUrlPath;
150  }  // setRestUrlPath Method
151
152
153  /**
154    * Get Method for class field 'restUrlPath_'.
155    *
156    * @return String - The value the class field 'restUrlPath_'.
157    *
158    **/
159  public String getRestUrlPath()
160  {
161    return restUrlPath_;
162  }  // getRestUrlPath Method
163
164
165  /** Check connectivity to the MusicBrainz URL specified by the class parms.
166    * @return true or false
167    **/
168  public boolean canConnect()
169  {
170    if(debugOut_) System.out.println(LOG_TAG+".canConnect("+getBaseUrl()+", "+getUsername()+", "+getPassword()+")");
171
172    boolean retVal = false;
173    if(isInit())
174    {
175      try
176      {
177        if(debugOut_) System.out.println(LOG_TAG+".init = true");
178        String usrlStr = (baseUrl_+"/release/"+DEFAULT_MUSICBRAINZ_TEST_RELEASE_MRID).replace(" " ,"%20");
179        URL url = new URL(usrlStr);
180        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
181        conn.setRequestMethod("GET");
182        if(acceptJSON_)
183          conn.setRequestProperty("Accept", "application/json");
184        else
185          conn.setRequestProperty("Accept", "application/xml");
186
187         conn.setRequestProperty("User-Agent", USER_AGENT+" ( tgutwin@webarts.ca )");
188
189        if (authenticating_)
190        {
191          //BASE64Encoder enc = new sun.misc.BASE64Encoder();
192          String userpassword = username_ + ":" + password_;
193          //String encodedAuthorization = android.util.Base64.encodeToString( userpassword.getBytes(), android.util.Base64.DEFAULT );
194          String encodedAuthorization = new String(Base64.encodeBase64( (userpassword.getBytes()) ));
195          conn.setRequestProperty("Authorization", "Basic "+ encodedAuthorization);
196        }
197
198        if (conn.getResponseCode() == 200)
199        {
200           retVal=true;
201        } // valid http response code
202        conn.disconnect();
203      }
204      catch (MalformedURLException e)
205      {
206       e.printStackTrace();
207      }
208      catch (IOException e)
209      {
210         e.printStackTrace();
211      }
212    }
213    return retVal;
214  }
215
216
217  /** returns the status for all the nodes. **/
218  public StringBuilder getStatus()
219  {
220    return serviceGet("/status");
221  }
222
223
224  /** queries all the nodes. **/
225  public StringBuilder getQuery()
226  {
227    return serviceGet("/query");
228  }
229
230
231
232
233  /** returns config. **/
234  public StringBuilder getConfig()
235  {
236    return serviceGet("/config");
237  }
238
239
240
241  /**
242   * Class main commandLine entry method that has a test command and some convienience commands, as well as a pure rest command.
243   **/
244  public static void main(String [] args)
245  {
246    final String methodName = CLASSNAME + ": main()";
247
248    MusicBrainzRestRequester instance = new MusicBrainzRestRequester();
249
250    /* Simple way af parsing the args */
251    if (args ==null || args.length<1)
252      System.out.println(getHelpMsgStr());
253    /* *************************************** */
254    else
255    {
256      if (args[0].equalsIgnoreCase("test"))
257      {
258        instance.debugOut_=false;
259
260        instance.setAcceptJSON(false); // flase means get XML
261        System.out.println("Can Connect? "+(instance.canConnect()?"Yes":"No"));
262        System.out.println("\n**************************\nLookup Barenaked Ladies - Gordon ");
263        String [] incs = {"artists"};
264        String releaseResults =  instance.lookupRelease(DEFAULT_MUSICBRAINZ_TEST_RELEASE_MRID, incs).toString();
265        String artistResults =  instance.lookupArtist(DEFAULT_MUSICBRAINZ_TEST_ARTIST_MRID).toString();
266        String searchResults = instance.searchRelease("Barenaked Ladies", "Gordon").toString();
267        try
268        {
269          System.out.println("\n\n"+"Release\n~~~~~~~~~~~~~~~~");
270          System.out.println(Util.xmlToPrettyString(releaseResults, 2));
271          nu.xom.Document releaseDoc = instance.parseXMLResponse(releaseResults);
272          System.out.println("\nRelease Date: "+instance.parseFirstReleaseDate(releaseDoc));
273          System.out.println("\nRelease Year: "+instance.parseFirstReleaseYear(releaseDoc));
274
275          System.out.println("\n\n"+"Artist\n~~~~~~~~~~~~~~~~");
276          System.out.println(Util.xmlToPrettyString(artistResults, 2));
277          nu.xom.Document artistDoc = instance.parseXMLResponse(artistResults);
278
279          System.out.println("\n\n"+"Search\n~~~~~~~~~~~~~~~~");
280          System.out.println(Util.xmlToPrettyString(searchResults, 2));
281          nu.xom.Document searchDoc = instance.parseXMLResponse(searchResults);
282        }
283        catch (Exception ex)
284        {
285          ex.printStackTrace();
286          System.out.println("\nParsing error - raw results:");
287          System.out.println(releaseResults);
288        }
289      }
290      /* *************************************** */
291      else if (args[0].equalsIgnoreCase("status"))
292      {
293        instance.getStatus();
294      }
295      /* *************************************** */
296      else if (args[0].equalsIgnoreCase("parseDir"))
297      {
298        System.out.println("    Parsing current dir AND creating meta-data files.");
299        instance.parseTunesDir(".");
300      }
301      /* *************************************** */
302      else
303      {
304        instance.restCMD(args);
305      }
306    }
307  } // main
308
309
310
311  /** Parses the tunes dir and reads in all the artists. **/
312  public void parseTunesDir(String tunesDirPath)
313  {
314      File[] artistDirFiles = (new File( tunesDirPath)).listFiles();
315      //java.util.List<Track> pagedLovedTracks = (java.util.List<Track>)lastFmLovedTracks_.getPageResults();
316      if(artistDirFiles!=null)
317      {
318        Vector <MusicbrainzArtist> artists = new Vector <MusicbrainzArtist>();
319        try
320        {
321          for(File currAristDirFile : artistDirFiles)
322            if(currAristDirFile.isDirectory()
323               && currAristDirFile.canRead() )
324            {
325              String artName = currAristDirFile.getAbsolutePath().substring(currAristDirFile.getAbsolutePath().lastIndexOf("/")+1);
326              // check if the artist dir has ANY album dirs, then add
327              File[] albumFiles = (new File(currAristDirFile.getAbsolutePath())).listFiles();
328              boolean valid = false;
329              try
330              {
331                for(int i=0; !valid&&i<albumFiles.length; i++)
332                {
333                  // check if there are ANY album dirs
334                  if(albumFiles[i].isDirectory()
335                     && albumFiles[i].canRead()
336                     && albumFiles[i].listFiles().length>0
337                    ) valid= true;
338                }
339                if(valid)
340                {
341                  MusicbrainzArtist art = new MusicbrainzArtist(Util.capsToSpacesInString(artName),
342                                                                currAristDirFile.getAbsolutePath());
343                  artists.add(art);
344                  art.writeMetaData();
345                  art.parseAlbumDirs();
346                }
347              }
348              catch ( Exception ex)
349              {
350                System.out.println(" *!What The Fudge (parseTunesDir()) : "+ex.getMessage());
351                ex.printStackTrace();
352              }
353            }
354        }
355        catch(Exception ex) { /* ignore error dirs */ }
356        artists.sort(MusicbrainzArtist.ArtistComparatorIgnoreCase );
357      }
358  }
359
360
361  /**
362   * commandLine command executor method for the test Command.
363   * @param args the array of commandLine args that got passed in
364   **/
365  protected void testCMD(String [] args)
366  {
367    final String methodName = CLASSNAME + ": testCMD(String [])";
368
369    System.out.println("Testing MusicBrainz Rest Service: "+ "/sys");
370    StringBuilder resp =  serviceGet("/sys");
371    System.out.println(resp.toString()); System.out.println();
372  }
373
374
375  protected StringBuilder lookupRelease(String mrid){return lookupRelease(mrid, null);}
376  /**
377   * commandLine command executor method for the test Command.
378   * @param mrid the MusicBrainz Release ID to lookup
379   * @param inc the optional array of associated includes to add to the lookup, null to not add any extra includes
380   **/
381  public StringBuilder lookupRelease(String mrid, String [] incs)
382  {
383    final String methodName = CLASSNAME + ": lookupRelease(String )";
384
385    if(debugOut_) System.out.println(" MusicBrainz Rest Service: "+ "lookup a RELEASE");
386    String inc = "";
387    if(incs!=null && incs.length >0 && !"".equals(incs[0]))
388    {
389      int cnt = 0;
390      inc = "?inc=";
391      for (String i : incs)
392      {
393        if(cnt++>0) inc+="+";
394        inc+=i;
395      }
396      if(debugOut_) System.out.println("  with includes="+inc);
397    }
398    StringBuilder resp =  serviceGet("/release/"+mrid+inc.replace(" " ,"+"));
399    //if(debugOut_) System.out.println(resp.toString()); if(debugOut_) System.out.println();
400    return resp;
401  }
402
403
404  /**
405   * Wraps the MusicBrainz search, specifically for a release ; example:  https://musicbrainz.org/ws/2/release/?query=release:Gordon%20AND%20artist:Barenaked%20Ladies .
406   * if you want only area=Canada : ID=71bbafaa-e825-3e15-8ca9-017dcad1748b<br>
407   * try https://musicbrainz.org/ws/2/release/?query=release:Gordon%20AND%20country:CA%20AND%20artist:Barenaked+Ladies
408   * @param Artist the artistName of teh release
409   * @param release the name of the release
410   * @return the search results in MMD - MusicBrainz XML Metadata Format (https://musicbrainz.org/doc/MusicBrainz_XML_Meta_Data)
411   **/
412  public StringBuilder searchRelease(String artist, String release)
413  {
414    final String methodName = CLASSNAME + ": searchRelease(String , String)";
415    StringBuilder resp = null;
416    //debugOut_=true;
417
418    if(debugOut_) System.out.println("  MusicBrainz Rest Service: "+ "search for a RELEASE");
419    if(artist!=null && release!=null
420       && !"".equals(artist) && !"".equals(release) )
421    {
422      try
423      {
424        String searchRequest = "/release/?query=release:"+
425                               URLEncoder.encode(release, "UTF-8")+
426                               "%20AND%20artist:"+
427                               URLEncoder.encode(artist, "UTF-8");
428        if(debugOut_) System.out.println("     searchRequest= "+searchRequest);
429        resp =  serviceGet(searchRequest);
430
431      }
432      catch(java.io.UnsupportedEncodingException ueEx)
433      {
434        ueEx.printStackTrace();
435      }
436    }
437    //if(debugOut_) System.out.println(resp.toString()); if(debugOut_) System.out.println();
438    //debugOut_=false;
439    return resp;
440  }
441
442
443  /**
444   * Wraps the MusicBrainz search, specifically for an Artist ; example:  https://musicbrainz.org/ws/2/release/?query=release:Gordon%20AND%20artist:Barenaked%20Ladies .
445   * if you want only area=Canada : ID=71bbafaa-e825-3e15-8ca9-017dcad1748b<br>
446   * try https://musicbrainz.org/ws/2/release/?query=release:Gordon%20AND%20country:CA%20AND%20artist:Barenaked+Ladies
447   * @param Artist the artistName of teh release
448   * @return the search results in MMD - MusicBrainz XML Metadata Format (https://musicbrainz.org/doc/MusicBrainz_XML_Meta_Data)
449   **/
450  public StringBuilder searchArtist(String artist)
451  {
452    final String methodName = CLASSNAME + ": searchArtist("+artist+")";
453    StringBuilder resp = null;
454
455    if(debugOut_) System.out.println("  MusicBrainz Rest Service: "+ "search for an ARTIST");
456    if(artist!=null
457       && !"".equals(artist) )
458    {
459      try
460      {
461        String searchRequest = "/artist/?query=artist:"+
462                               URLEncoder.encode(artist, "UTF-8");
463        if(debugOut_) System.out.println("     searchRequest= "+searchRequest);
464        resp =  serviceGet(searchRequest);
465
466      }
467      catch(java.io.UnsupportedEncodingException ueEx)
468      {
469        ueEx.printStackTrace();
470      }
471    }
472    //if(debugOut_) System.out.println(resp.toString()); if(debugOut_) System.out.println();
473    //debugOut_=false;
474    return resp;
475  }
476
477
478  protected StringBuilder lookupArtist(String mrid){return lookupArtist(mrid, null);}
479  /**
480   * commandLine command executor method for the test Command.
481   * @param mrid the MusicBrainz Release ID to lookup
482   * @param inc the optional array of associated includes to add to the lookup, null to not add any extra includes
483   **/
484  public StringBuilder lookupArtist(String mrid, String [] incs)
485  {
486    final String methodName = CLASSNAME + ": lookupArtist(String )";
487
488    if(debugOut_) System.out.println(" MusicBrainz Rest Service: "+ "lookup an ARTIST");
489    String inc = "";
490    if(incs!=null && incs.length >0 && !"".equals(incs[0]))
491    {
492      int cnt = 0;
493      inc = "?inc=";
494      for (String i : incs)
495      {
496        if(cnt++>0) inc+="+";
497        inc+=i;
498      }
499    }
500    StringBuilder resp =  serviceGet("/artist/"+mrid+inc.replace(" " ,"+"));
501    //if(debugOut_) System.out.println(resp.toString()); if(debugOut_) System.out.println();
502    return resp;
503  }
504
505
506  public nu.xom.Document parseXMLResponse(String respXmlStr)
507  {
508    //String methodName = className_+"."+Util.getCurrentMethodName();
509    //log_.startMethod(methodName);
510    if(debugOut_) System.out.println("  parseXMLResponse(String) ");
511
512    nu.xom.Document respDoc = null;
513    nu.xom.Element rootElem = null;
514    try
515    {
516      respDoc = xmlBuilder_.build(respXmlStr,null);
517    }
518    catch (nu.xom.ParsingException xomEx)
519    {
520      xomEx.printStackTrace();
521      System.out.println("MAJor Error: parsing respXmlStr");
522      System.out.println(respXmlStr);
523    }
524    catch (IOException ioEx)
525    {
526      ioEx.printStackTrace();
527      System.out.println("MAJor Error: IOException respXmlStr");
528      System.out.println(respXmlStr);
529    }
530    return respDoc;
531  }
532
533
534  public java.util.Date parseFirstReleaseDate(nu.xom.Document respDoc){return parseReleaseDate(respDoc, 0);}
535  public java.util.Date parseReleaseDate(nu.xom.Document respDoc, int releaseIndex)
536  {
537    java.util.Date retVal = null;
538    nu.xom.Element rootElem = null;
539    nu.xom.Element dateXpathElem = null;
540    String dateStr = "";
541
542    if(debugOut_) System.out.println(" > parseReleaseDate not null?"+(respDoc!=null));
543    if (respDoc!=null)
544    {
545      rootElem = respDoc.getRootElement();
546
547      nu.xom.Elements releaseEventElems = parseReleaseEventElems(respDoc);
548      nu.xom.Element releaseEventElem = null;  //("release-event");
549      int numReleases = 0;
550      for (int i=0; i< releaseEventElems.size(); i++)
551      {
552        releaseEventElem = releaseEventElems.get(i);  //release-event
553        if(releaseEventElem.getLocalName().equalsIgnoreCase("release-event") )
554        {
555          if(numReleases==releaseIndex) i=releaseEventElems.size();
556          numReleases++;
557        }
558      }
559      if (releaseEventElem!=null )
560      {
561        if(debugOut_) System.out.print(" .");
562        nu.xom.Elements dateElems = releaseEventElem.getChildElements();
563        nu.xom.Element dateElem = dateElems.get(0);  //("date");
564        dateStr = dateElem.getValue();
565        if(debugOut_) System.out.print(" "+dateStr);
566        if(debugOut_) System.out.println("  "+Integer.parseInt(dateStr.substring(0,4))+" "+
567                                Integer.parseInt(dateStr.substring(5,7))+" "+
568                                Integer.parseInt(dateStr.substring(8,10)));
569        if (dateElem!=null ) retVal = new java.util.Date(Integer.parseInt(dateStr.substring(0,4))-1900,
570                                                         Integer.parseInt(dateStr.substring(5,7)),
571                                                         Integer.parseInt(dateStr.substring(8,10)));
572      }
573    }
574    if(debugOut_) System.out.println();
575    return retVal;
576  }
577
578
579  public int parseFirstReleaseYear(nu.xom.Document respDoc){return parseReleaseYear(respDoc, 0);}
580  public int parseReleaseYear(nu.xom.Document respDoc, int releaseIndex)
581  {
582     int retVal = 0;
583    nu.xom.Element rootElem = null;
584
585    if(debugOut_) System.out.println(" > parseReleaseYear not null?"+(respDoc!=null));
586    if (respDoc!=null)
587    {
588      nu.xom.Elements releaseEventElems = parseReleaseEventElems(respDoc);
589      nu.xom.Element releaseEventElem = null;  //("release-event");
590      for (int i=0; i< releaseEventElems.size(); i++)
591      {
592        releaseEventElem = releaseEventElems.get(i);  //release-event
593        if(releaseEventElem.getLocalName().equalsIgnoreCase("release-event"))
594          i=releaseEventElems.size();
595      }
596      if (releaseEventElem!=null )
597      {
598        if(debugOut_) System.out.print(" .");
599        nu.xom.Elements dateElems = releaseEventElem.getChildElements();
600        nu.xom.Element dateElem = dateElems.get(0);  //("date");
601        if (dateElem!=null )
602        {
603          String dateStr = dateElem.getValue();
604          if(debugOut_) System.out.print(" "+dateStr);
605          retVal = Integer.parseInt(dateStr.substring(0,4));
606        }
607      }
608    }
609    if(debugOut_) System.out.println();
610    return retVal;
611  }
612
613
614  public nu.xom.Elements parseReleaseEventElems(nu.xom.Document respDoc)
615  {
616    nu.xom.Elements retVal = null;
617    nu.xom.Element rootElem = null;
618
619    if(debugOut_) System.out.println(" > parseReleaseEventElems not null?"+(respDoc!=null));
620    if (respDoc!=null)
621    {
622      rootElem = respDoc.getRootElement();
623      // load the data
624      if (rootElem != null && rootElem instanceof Element && rootElem.getLocalName().equals("metadata"))
625      {
626        if(debugOut_) System.out.print(" .");
627        nu.xom.Elements releaseElems = rootElem.getChildElements();
628        nu.xom.Element releaseElem = releaseElems.get(0);
629        if (releaseElem!=null )
630        {
631          if(debugOut_) System.out.print(" .");
632          nu.xom.Elements releaseEventListElems = releaseElem.getChildElements();
633          nu.xom.Element releaseEventListElem = null;
634          for (int i=0; i< releaseEventListElems.size(); i++)
635          {
636            releaseEventListElem = releaseEventListElems.get(i);  //release-event-list
637            if(releaseEventListElem.getLocalName().equalsIgnoreCase("release-event-list"))
638              i=releaseEventListElems.size();
639            //releaseEventListElem = releaseElem.getFirstChildElement("release-event-list");
640          }
641          if (releaseEventListElem!=null )
642          {
643            if(debugOut_) System.out.print(" ." );
644            retVal = releaseEventListElem.getChildElements();
645          }
646        }
647      }
648    }
649    return retVal;
650  }
651
652
653  public nu.xom.Element parseFirstReleaseEventElem(nu.xom.Document respDoc){return parseReleaseEventElem(respDoc, 0);}
654  public nu.xom.Element parseReleaseEventElem(nu.xom.Document respDoc, int releaseIndex)
655  {
656    nu.xom.Element retVal = null;
657    nu.xom.Element rootElem = null;
658
659    if(debugOut_) System.out.println(" > parseReleaseEventElem not null?"+(respDoc!=null));
660    if (respDoc!=null)
661    {
662      rootElem = respDoc.getRootElement();
663
664      nu.xom.Elements releaseEventElems = parseReleaseEventElems(respDoc);
665      nu.xom.Element releaseEventElem = null;  //("release-event");
666      int numReleases = 0;
667      for (int i=0; i< releaseEventElems.size(); i++)
668      {
669        releaseEventElem = releaseEventElems.get(i);  //release-event
670        if(releaseEventElem.getLocalName().equalsIgnoreCase("release-event") )
671        {
672          if(numReleases==releaseIndex)
673          {
674            i=releaseEventElems.size();
675            retVal = releaseEventElem;
676          }
677          numReleases++;
678        }
679      }
680    }
681
682    return retVal;
683  }
684
685
686  public int parseSearchResultsForReleaseYear(nu.xom.Document respDoc)
687  {
688    int retVal = 1900;
689    nu.xom.Element rootElem = null;
690
691    if(debugOut_) System.out.println(" > parseReleaseEventElems not null?"+(respDoc!=null));
692    if (respDoc!=null)
693    {
694      rootElem = respDoc.getRootElement();
695      // load the data
696      if (rootElem != null && rootElem instanceof Element && rootElem.getLocalName().equals("metadata"))
697      {
698        if(debugOut_) System.out.print(" .");
699        nu.xom.Elements releaseListElems = rootElem.getChildElements();
700        nu.xom.Element releaseListElem = releaseListElems.get(0);
701        if (releaseListElem!=null )
702        {
703          if(debugOut_) System.out.print(" .");
704          nu.xom.Elements releaseElems = releaseListElem.getChildElements();
705          nu.xom.Element releaseElem = null;
706          //for (int i=0; i< releaseElems.size(); i++)
707          //{
708          releaseElem = releaseElems.get(0);  //release-event-list
709          if (releaseElem!=null )
710          {
711            if(debugOut_) System.out.print(" .");
712            if(debugOut_) System.out.print(" ("+releaseElem.getLocalName()+")");
713            nu.xom.Elements releaseSubElems = releaseElem.getChildElements();
714            nu.xom.Element dateElem = null;
715            boolean foundIt = false;
716            for (int i=0; i< releaseSubElems.size(); i++)
717            {
718              dateElem = releaseSubElems.get(i);
719              if(dateElem.getLocalName().equalsIgnoreCase("date"))
720              {
721                foundIt = true;
722                i=releaseSubElems.size();
723              }
724            }
725            if (foundIt)
726            {
727              if(debugOut_) System.out.print(" ." );
728              String dateStr = dateElem.getValue();
729              if(debugOut_) System.out.print(" "+dateStr);
730              retVal = Integer.parseInt(dateStr.substring(0,4));
731            }
732          }
733        }
734      }
735    }
736    if(debugOut_) System.out.println("\n  >>>>>>>>>>>>>>>>>>>>>>>>>>");
737   return retVal;
738  }
739
740
741  public nu.xom.Element parseSearchResultsForFirstReleaseElem(nu.xom.Document respDoc)
742  {
743    return parseSearchResultsForReleaseElem(respDoc, 0);
744  }
745
746
747  public nu.xom.Element parseSearchResultsForReleaseElem(nu.xom.Document respDoc, int releaseIndex)
748  {
749    nu.xom.Element retVal = null;
750    nu.xom.Element rootElem = null;
751
752    if(debugOut_) System.out.println(" > parseSearchResultsForReleaseElem["+releaseIndex+"] not null?"+(respDoc!=null));
753    if (respDoc!=null)
754    {
755      rootElem = respDoc.getRootElement();
756      // load the data
757      if (rootElem != null && rootElem instanceof Element && rootElem.getLocalName().equals("metadata"))
758      {
759        if(debugOut_) System.out.print(" .");
760        try
761        {
762          nu.xom.Elements releaseListElems = rootElem.getChildElements();
763          nu.xom.Element releaseListElem = releaseListElems.get(0);
764          if (releaseListElem!=null )
765          {
766            if(debugOut_) System.out.print(" .");
767            nu.xom.Elements releaseElems = releaseListElem.getChildElements();
768            nu.xom.Element releaseElem = null;
769            releaseElem = releaseElems.get(releaseIndex);  //release
770
771            if(debugOut_) System.out.print(" . found: "+releaseElem.getLocalName());
772            retVal = releaseElem;
773          }
774        }
775        catch (Exception ex)
776        {
777          System.out.print(" NOT found: ["+releaseIndex+"]");
778          retVal = null;
779          ex.printStackTrace();
780        }
781      }
782    }
783    if(debugOut_) System.out.println("\n  >>>>>>>>>>>>>>>>>>>>>>>>>>");
784   return retVal;
785  }
786
787
788  /**
789    * Parses through the Release search results doc, gets the 1st release
790    * and looks for the status subElement.
791    *
792    **/
793  public String parseSearchResultsForFirstReleaseStatus(nu.xom.Document respDoc)
794  {
795    return parseSearchResultsForReleaseStatus(respDoc, 0);
796  }
797
798
799  /**
800    * Parses through the Release search results doc, gets the 1st release
801    * and looks for the status subElement.
802    *
803    **/
804  public String parseSearchResultsForReleaseStatus(nu.xom.Document respDoc, int releaseIndex)
805  {
806    return parseSearchResultsForReleaseSubElement(respDoc, releaseIndex, "status");
807  }
808
809
810  /**
811    * Parses through the Release search results doc, gets the 1st release
812    * and looks for the subElement named elemntName passed into this method.
813    *
814    **/
815  public String parseSearchResultsForFirstReleaseSubElement(nu.xom.Document respDoc, String elementName)
816  {
817    return parseSearchResultsForReleaseSubElement(respDoc, 0, elementName);
818  }
819
820
821  /**
822    * Parses through the search results doc, gets the releaseIndex specified release
823    * and looks for the subElement named elemntName passed into this method.
824    *
825    **/
826  public String parseSearchResultsForReleaseSubElement(nu.xom.Document respDoc, int releaseIndex, String elementName)
827  {
828    String retVal = null;
829    nu.xom.Element rootElem = null;
830
831    if(debugOut_) System.out.println(" > parseSearchResultsForReleaseSubElement not null?"+(respDoc!=null));
832    if (respDoc!=null)
833    {
834      rootElem = respDoc.getRootElement();
835      // load the data
836      if (rootElem != null && rootElem instanceof Element && rootElem.getLocalName().equals("metadata"))
837      {
838        if(debugOut_) System.out.print(" .");
839        nu.xom.Elements releaseListElems = rootElem.getChildElements();
840        nu.xom.Element releaseListElem = releaseListElems.get(0);
841        if (releaseListElem!=null )
842        {
843          if(debugOut_) System.out.print(" .");
844          nu.xom.Elements releaseElems = releaseListElem.getChildElements();
845          nu.xom.Element releaseElem = null;
846          //for (int i=0; i< releaseElems.size(); i++)
847          //{
848          releaseElem = releaseElems.get(releaseIndex);  //release-event-list
849          if (releaseElem!=null )
850          {
851            if(debugOut_) System.out.print(" .");
852            if(debugOut_) System.out.print(" ( "+releaseElem.getLocalName());
853            nu.xom.Elements releaseSubElems = releaseElem.getChildElements();
854            nu.xom.Element subElem = null;
855            boolean foundIt = false;
856            for (int i=0; i< releaseSubElems.size(); i++)
857            {
858              subElem = releaseSubElems.get(i);
859              if(subElem.getLocalName().equalsIgnoreCase(elementName))
860              {
861                foundIt = true;
862                i=releaseSubElems.size();
863              }
864            }
865            if (foundIt)
866            {
867              if(debugOut_) System.out.print("/"+elementName+" ) - " );
868              String elemValueStr = subElem.getValue();
869              if(debugOut_) System.out.print(" "+elemValueStr);
870              retVal = elemValueStr;
871            }
872          }
873        }
874      }
875    }
876    if(debugOut_) System.out.println("\n  >>>>>>>>>>>>>>>>>>>>>>>>>>");
877   return retVal;
878  }
879
880
881  /**
882   * commandLine command executor method for the default rest Command.
883   * It treats each arg as a part of a single rest command and passes it along to the ISY.
884   * @param args the array of commandLine args that got passed in
885   **/
886  protected void restCMD(String [] args)
887  {
888    final String methodName = CLASSNAME + ": restCMD(String [])";
889    // Parse the command
890    String allcommands = args[0];
891    for (int i=1;i< args.length;i++) allcommands+=" "+args[i];
892    if (debugOut_) System.out.print("Sending MusicBrainz Rest Service: "+allcommands);
893    String passedCommand = (allcommands.startsWith(restUrlPath_+"/")?allcommands.substring(restUrlPath_.length()):allcommands);
894    if(debugOut_) System.out.println(" ("+passedCommand+")");
895    passedCommand = (passedCommand.startsWith("/")?passedCommand:"/"+passedCommand);
896    StringBuilder resp =  serviceGet(passedCommand);
897    if (resp!=null)
898    {
899      System.out.println(responseIndenter(resp).toString());
900      System.out.println();
901    }
902    else
903    {
904      System.out.println("Response Error");
905      System.out.println();
906    }
907
908  }
909
910
911  /**
912   * Template method for future commandLine command executor methods.
913   * @param args the array of commandLine args that got passed in
914   **/
915  protected void templateCMD(String [] args)
916  {
917    final String methodName = CLASSNAME + ": testCMD(String [])";
918
919  }
920
921
922    /** gets the help as a String.
923   * @return the helpMsg in String form
924   **/
925  protected static String getHelpMsgStr() {return getHelpMsg().toString();}
926
927
928  /** Makes the JSON string pretty with indenting. **/
929  public static String prettyJson(String jsonStr)
930  {
931    String retVal = jsonStr;
932    retVal = retVal.replace("[", " [\n");
933    retVal = retVal.replace("]", "  ]" + SYSTEM_LINE_SEPERATOR);
934    retVal = retVal.replace("]\n\"", "  ]\"" + SYSTEM_LINE_SEPERATOR);
935    retVal = retVal.replace("{", "  {" + SYSTEM_LINE_SEPERATOR + "      ");
936    retVal = retVal.replace("}", "}" + SYSTEM_LINE_SEPERATOR);
937    retVal = retVal.replace(",", "," + SYSTEM_LINE_SEPERATOR + "      ");
938    retVal = retVal.replace("}" + SYSTEM_LINE_SEPERATOR + "," + SYSTEM_LINE_SEPERATOR, "    }," + SYSTEM_LINE_SEPERATOR);
939    retVal = retVal.replace("[\n", "  [ ");
940    retVal = retVal.replace("        {", "    {");
941    retVal = retVal.replace("[   {", "[\n   {");
942    retVal = retVal.replace("\n}", "\n    }");
943    return retVal;
944  }
945
946
947
948  /** initializes and gets the helpMsg_
949  class var.
950   * @return the class var helpMsg_
951   **/
952  protected static StringBuilder getHelpMsg()
953  {
954    helpMsg_ = new StringBuilder(SYSTEM_LINE_SEPERATOR);
955    helpMsg_.append("---  WebARTS "+CLASSNAME+" Class  -----------------------------------------------------");
956    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
957    helpMsg_.append("--- + $Revision: 1219 $ $Date: 2018-03-02 22:12:45 -0800 (Fri, 02 Mar 2018) $ ---");
958    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
959    helpMsg_.append("-------------------------------------------------------------------------------");
960    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
961    helpMsg_.append("WebARTS ca.bc.webarts.tools.MusicBrainzRestRequester Class");
962    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
963    helpMsg_.append("SYNTAX:");
964    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
965    helpMsg_.append("   java ");
966    helpMsg_.append(CLASSNAME);
967    helpMsg_.append(" command or {restCommand}");
968    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
969    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
970    helpMsg_.append("Available commands:");
971    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
972    helpMsg_.append("    test");
973    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
974    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
975    helpMsg_.append("    parseDir");
976    helpMsg_.append("             Creates MusicBrainz Meta-data files for all subdirectories.");
977    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
978    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
979    helpMsg_.append("Available restCommands:");
980    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
981    helpMsg_.append("    see: https://musicbrainz.org/doc/Development/XML_Web_Service/Version_2");
982    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
983    helpMsg_.append("  Example: java ca.bc.webarts.MusicBrainzRestRequester test ");
984    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
985    helpMsg_.append("---------------------------------------------------------");
986    helpMsg_.append("----------------------");
987    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
988
989    return helpMsg_;
990  }
991
992}
993
994  /**
995    *
996    *
997    * Musicbrainz Artist Class Object<br>
998    * ~~~~~~~~~~~~~~~~
999    * representing the returned XML from the mb api query
1000    *<pre>
1001        <?xml version="1.0" encoding="UTF-8"?>
1002        <metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#">
1003          <artist id="86e736b4-93e2-40ff-9e1c-fb7c63fef5f6" type="Group" type-id="e431f5f6-b5d2-343d-8b36-72607fffb74b">
1004            <name>Barenaked Ladies</name>
1005            <sort-name>Barenaked Ladies</sort-name>
1006            <isni-list>
1007              <isni>0000000109413643</isni>
1008            </isni-list>
1009            <country>CA</country>
1010            <area id="71bbafaa-e825-3e15-8ca9-017dcad1748b">
1011              <name>Canada</name>
1012              <sort-name>Canada</sort-name>
1013              <iso-3166-1-code-list>
1014                <iso-3166-1-code>CA</iso-3166-1-code>
1015              </iso-3166-1-code-list>
1016            </area>
1017            <begin-area id="74b24e62-d2fe-42d2-9d96-31f2da756c77">
1018              <name>Toronto</name>
1019              <sort-name>Toronto</sort-name>
1020            </begin-area>
1021            <life-span>
1022              <begin>1988</begin>
1023            </life-span>
1024          </artist>
1025        </metadata>
1026     </pre>
1027    *
1028    **/
1029  class MusicbrainzArtist implements Comparable<MusicbrainzArtist>
1030  {
1031    boolean debugOut_ = MusicBrainzRestRequester.debugOut_;
1032    String name = "";
1033    String id = "";
1034    String sortName = "";
1035    String country = "";
1036    MusicbrainzArea area = null;
1037    String metaDataDirFilename_ = "";
1038    String metaDataFilename_ = "mbData.props";
1039
1040    String propsString = "";
1041    String mbSearchResults_ = "";
1042    nu.xom.Document mbSearchResultsDoc_ = null;
1043
1044
1045    public MusicbrainzArtist(String artName)
1046    {
1047      name = artName.trim();
1048      sortName = name;
1049    }
1050
1051
1052    public MusicbrainzArtist(String artName, String metaDataDirFilename)
1053    {
1054      if(debugOut_) System.out.println("\n >>> Creating NEW MusicbrainzArtist\n---------------------------------\n");
1055      name = artName.trim();
1056      sortName = name;
1057      metaDataDirFilename_ = metaDataDirFilename;
1058      MusicBrainzRestRequester mbRR = new MusicBrainzRestRequester();
1059      mbSearchResults_ = mbRR.searchArtist(name).toString();
1060      try
1061      {
1062        mbSearchResultsDoc_ = mbRR.parseXMLResponse(mbSearchResults_);
1063        parseFirstArtistElem(mbSearchResultsDoc_);
1064        if(debugOut_) System.out.println("  MB Search Results:\n");
1065        if(debugOut_) System.out.println(Util.xmlToPrettyString(mbSearchResults_, 2));
1066        //parseAlbumDirs();
1067      }
1068      catch (Exception ex)
1069      {
1070        ex.printStackTrace();
1071        System.out.println("\nParsing error - raw results:");
1072        System.out.println(mbSearchResults_);
1073      }
1074    }
1075
1076
1077    public String toString(){return name;}
1078
1079
1080  /**
1081    * Set Method for class field 'sortName'.
1082    *
1083    * @param sortName is the value to set this class field to.
1084    *
1085    **/
1086  public  void setSortName(String sortName)
1087  {
1088    this.sortName = sortName;
1089  }  // setSortName Method
1090
1091
1092  /**
1093    * Get Method for class field 'sortName'.
1094    *
1095    * @return String - The value the class field 'sortName'.
1096    *
1097    **/
1098  public String getSortName()
1099  {
1100    return sortName;
1101  }  // getSortName Method
1102
1103
1104  public String getPropsString()
1105  {
1106    String p = "MusicbrainzArtist_";
1107    String retVal = "#MusicBrainz Artist Properties\n";
1108    retVal += "# "+Util.createCurrentDateTime()+"\n";
1109    retVal += p+"name="+name+"\n";
1110    retVal += p+"sortName="+getSortName()+"\n";
1111    retVal += p+"id="+id+"\n";
1112    retVal += p+"country="+country+"\n";
1113    retVal += p+"metaDataDirFilename="+metaDataDirFilename_+"\n";
1114    retVal += area.getPropsString();
1115
1116    return retVal;
1117  }
1118
1119
1120  public void writeMetaData()
1121  {
1122    System.out.println("\nCreating Metadata file for: "+getSortName());
1123    System.out.println("  Filename: "+metaDataDirFilename_+"/"+metaDataFilename_);
1124    System.out.println(getPropsString());
1125    if(MusicBrainzRestRequester.doWrites_) Util.writeStringToFile(getPropsString(), metaDataDirFilename_+"/"+metaDataFilename_);
1126  }
1127
1128
1129  public nu.xom.Element parseFirstArtistElem(nu.xom.Document respDoc){return parseArtistElem(respDoc, 0);}
1130  public nu.xom.Element parseArtistElem(nu.xom.Document respDoc, int artistIndex)
1131  {
1132    nu.xom.Element retVal = null;
1133    nu.xom.Element rootElem = null;
1134
1135    if(debugOut_) System.out.println("  > parseArtistElem not null?"+(respDoc!=null));
1136    if (respDoc!=null)
1137    {
1138      rootElem = respDoc.getRootElement();
1139      if (rootElem != null && rootElem instanceof Element && rootElem.getLocalName().equals("metadata"))
1140      {
1141        if(debugOut_) System.out.print(" .");
1142        nu.xom.Elements artistListElems = rootElem.getChildElements();
1143        nu.xom.Element artistListElem = artistListElems.get(0);
1144        if (artistListElem!=null )
1145        {
1146          nu.xom.Elements artistElems = artistListElem.getChildElements();
1147          int numArtists = 0;
1148          if (artistElems!=null )
1149          {
1150            nu.xom.Element artistElem = null;
1151            for (int i=0; i< artistListElems.size(); i++)
1152            {
1153              if(debugOut_) System.out.print(" .");
1154              artistElem = artistElems.get(i);
1155              if(artistElem.getLocalName().equalsIgnoreCase("artist") )
1156              {
1157                if(debugOut_) System.out.print(" .");
1158                if(numArtists==artistIndex)
1159                {
1160                  i=artistListElems.size();
1161                  retVal = artistElem;
1162                  id=artistElem.getAttributeValue("id");
1163
1164                  nu.xom.Elements artistSubElems = artistElem.getChildElements();
1165                  if (artistSubElems!=null )
1166                  {
1167                    nu.xom.Element currElem = null;
1168                    for (int ii=0; ii< artistSubElems.size(); ii++)
1169                    {
1170                      currElem = artistSubElems.get(ii);
1171                      if(currElem.getLocalName().equalsIgnoreCase("sort-name") ) sortName = currElem.getValue();
1172                      if(currElem.getLocalName().equalsIgnoreCase("country") ) country = currElem.getValue();
1173                      if(currElem.getLocalName().equalsIgnoreCase("area") )
1174                      {
1175                        //parse the area
1176                        area = new MusicbrainzArea(currElem);
1177                      }
1178                      //if(debugOut_) System.out.print("      subElement "+currElem.getLocalName());
1179                    }
1180                  }
1181                }
1182                numArtists++;
1183              }
1184              else
1185                if(debugOut_) System.out.print(" !!! "+artistElem.getLocalName());
1186            }
1187          }
1188        }
1189      }
1190    }
1191
1192    return retVal;
1193  }
1194
1195
1196    public void parseAlbumDirs()
1197    {
1198      File[] albumDirFiles = (new File( metaDataDirFilename_ )).listFiles();
1199      //java.util.List<Track> pagedLovedTracks = (java.util.List<Track>)lastFmLovedTracks_.getPageResults();
1200      boolean valid = false;
1201      if(albumDirFiles!=null)
1202      {
1203        Vector <MusicbrainzRelease> albums = new Vector <MusicbrainzRelease>();
1204        try
1205        {
1206          for(File currAlbumDirFile : albumDirFiles)
1207          {
1208            valid = false;
1209            if(currAlbumDirFile.isDirectory()
1210               && currAlbumDirFile.canRead()
1211               && currAlbumDirFile.listFiles().length>0
1212               ) valid= true;
1213            {
1214              if(valid)
1215              {
1216                String albName = currAlbumDirFile.getAbsolutePath().substring(currAlbumDirFile.getAbsolutePath().lastIndexOf("/")+1);
1217                MusicbrainzRelease alb = new MusicbrainzRelease(name,
1218                                                                Util.capsToSpacesInString(albName),
1219                                                                currAlbumDirFile.getAbsolutePath());
1220                albums.add(alb);
1221                alb.writeMetaData();
1222              }
1223            }
1224          }
1225        }
1226        catch ( Exception ex)
1227        {
1228          System.out.println(" *!What The Fudge (parseAlbumDirs()) : "+ex.getMessage());
1229          ex.printStackTrace();
1230        }
1231        albums.sort(MusicbrainzRelease.ReleaseComparatorIgnoreCase );
1232      }
1233    }
1234
1235
1236    /** Comparator for ignore case sort. **/
1237    public int compareToIgnoreCase(MusicbrainzArtist other)
1238    {
1239      return this.getSortName().compareToIgnoreCase(other.getSortName());
1240    }
1241
1242
1243    /** implements Comparator. **/
1244    @Override public int compareTo(MusicbrainzArtist other)
1245    {
1246      return this.getSortName().compareTo(other.getSortName());
1247    }
1248
1249
1250    /** A Comparator that can be used to sort Artist vectors. **/
1251    public static Comparator <MusicbrainzArtist> ArtistComparator = new Comparator<MusicbrainzArtist>()
1252      {
1253        @Override public int compare(MusicbrainzArtist one, MusicbrainzArtist two)
1254        {
1255          return one.getSortName().compareTo(two.getSortName());
1256        }
1257      };
1258
1259
1260    /** A case in-sensitive Comparator that can be used to sort MusicbrainzArtist vectors. **/
1261    public static Comparator <MusicbrainzArtist> ArtistComparatorIgnoreCase = new Comparator<MusicbrainzArtist>()
1262      {
1263        @Override public int compare(MusicbrainzArtist one, MusicbrainzArtist two)
1264        {
1265          //return one.name(true).compareTo(two.name(true));
1266          return one.getSortName().compareToIgnoreCase(two.getSortName());
1267        }
1268      };
1269
1270  }
1271
1272
1273  /**
1274    *
1275    *
1276    * Musicbrainz Release Class Object<br>
1277    * ~~~~~~~~~~~~~~~~
1278    * representing the returned XML from the mb api query
1279    *<pre>
1280        <?xml version="1.0" encoding="UTF-8"?>
1281
1282     </pre>
1283    *
1284    **/
1285  class MusicbrainzRelease implements Comparable<MusicbrainzRelease>
1286  {
1287    boolean debugOut_ = MusicBrainzRestRequester.debugOut_;
1288    String artistName_ = "";
1289    String name = "";
1290    String id = "";
1291    String sortName = "";
1292    String country = "";
1293    String releaseDate = "";
1294    int year = 0;
1295    String status = "";
1296    MusicbrainzArea area = null;
1297    String metaDataDirFilename_ = "";
1298    String metaDataFilename_ = "mbData.props";
1299
1300    String propsString = "";
1301    String mbSearchResults_ = "";
1302    nu.xom.Document mbSearchResultsDoc_ = null;
1303
1304
1305    public MusicbrainzRelease(String artistName, String albName)
1306    {
1307      artistName_ = artistName;
1308      name = albName.trim();
1309      sortName = name;
1310    }
1311
1312
1313    public MusicbrainzRelease(String artistName, String albName, String metaDataDirFilename)
1314    {
1315      artistName_ = artistName;
1316      name = albName.trim();
1317      sortName = name;
1318      metaDataDirFilename_ = metaDataDirFilename;
1319      MusicBrainzRestRequester mbRR = new MusicBrainzRestRequester();
1320      mbSearchResults_ = mbRR.searchRelease(artistName, name).toString();
1321      try
1322      {
1323        mbSearchResultsDoc_ = mbRR.parseXMLResponse(mbSearchResults_);
1324        nu.xom.Element firstRelease = mbRR.parseSearchResultsForFirstReleaseElem(mbSearchResultsDoc_);
1325        id=firstRelease.getAttributeValue("id");
1326        if(debugOut_) System.out.println("  Release ID: "+ id);
1327        if(debugOut_) System.out.println("  MB Search Results:\n");
1328        if(debugOut_) System.out.println(Util.xmlToPrettyString(mbSearchResults_, 2));
1329        status=mbRR.parseSearchResultsForFirstReleaseStatus(mbSearchResultsDoc_);
1330        country=mbRR.parseSearchResultsForReleaseSubElement(mbSearchResultsDoc_, 0, "country");
1331        releaseDate=mbRR.parseSearchResultsForReleaseSubElement(mbSearchResultsDoc_, 0, "date");
1332        year = Integer.parseInt(releaseDate.substring(0,4));
1333      }
1334      catch (Exception ex)
1335      {
1336        ex.printStackTrace();
1337        System.out.println("\n---------------------------------\nParsing error\n - raw results:");
1338        try {System.out.println(Util.xmlToPrettyString(mbSearchResults_, 2));}
1339        catch (Exception exx) {System.out.println(mbSearchResults_);}
1340      }
1341    }
1342
1343
1344    public String toString(){return name;}
1345
1346
1347  /**
1348    * Set Method for class field 'sortName'.
1349    *
1350    * @param sortName is the value to set this class field to.
1351    *
1352    **/
1353  public  void setSortName(String sortName)
1354  {
1355    this.sortName = sortName;
1356  }  // setSortName Method
1357
1358
1359  /**
1360    * Get Method for class field 'sortName'.
1361    *
1362    * @return String - The value the class field 'sortName'.
1363    *
1364    **/
1365  public String getSortName()
1366  {
1367    return sortName;
1368  }  // getSortName Method
1369
1370
1371   public String getPropsString()
1372  {
1373    String p = "MusicbrainzRelease_";
1374    String retVal = "#MusicBrainz Album Properties\n";
1375    retVal += "# "+Util.createCurrentDateTime()+"\n";
1376    retVal += p+"artistName="+artistName_+"\n";
1377    retVal += p+"name="+name+"\n";
1378    retVal += p+"sortName="+getSortName()+"\n";
1379    retVal += p+"id="+id+"\n";
1380    retVal += p+"country="+country+"\n";
1381    retVal += p+"date="+releaseDate+"\n";
1382    retVal += p+"year="+year+"\n";
1383    retVal += p+"status="+status+"\n";
1384    retVal += p+"metaDataDirFilename="+metaDataDirFilename_+"\n";
1385
1386    return retVal;
1387  }
1388
1389
1390  public void writeMetaData()
1391  {
1392    System.out.println("\nCreating Metadata file for: "+getSortName());
1393    System.out.println("  Filename: "+metaDataDirFilename_+"/"+metaDataFilename_);
1394    System.out.println(getPropsString());
1395    if(MusicBrainzRestRequester.doWrites_) Util.writeStringToFile(getPropsString(), metaDataDirFilename_+"/"+metaDataFilename_);
1396  }
1397
1398
1399    /** Comparator for ignore case sort. **/
1400    public int compareToIgnoreCase(MusicbrainzRelease other)
1401    {
1402      return this.getSortName().compareToIgnoreCase(other.getSortName());
1403    }
1404
1405
1406    /** implements Comparator. **/
1407    @Override public int compareTo(MusicbrainzRelease other)
1408    {
1409      return this.getSortName().compareTo(other.getSortName());
1410    }
1411
1412
1413    /** A Comparator that can be used to sort Release vectors. **/
1414    public static Comparator <MusicbrainzRelease> ReleaseComparator = new Comparator<MusicbrainzRelease>()
1415      {
1416        @Override public int compare(MusicbrainzRelease one, MusicbrainzRelease two)
1417        {
1418          return one.getSortName().compareTo(two.getSortName());
1419        }
1420      };
1421
1422
1423    /** A case in-sensitive Comparator that can be used to sort MusicbrainzRelease vectors. **/
1424    public static Comparator <MusicbrainzRelease> ReleaseComparatorIgnoreCase = new Comparator<MusicbrainzRelease>()
1425      {
1426        @Override public int compare(MusicbrainzRelease one, MusicbrainzRelease two)
1427        {
1428          //return one.name(true).compareTo(two.name(true));
1429          return one.getSortName().compareToIgnoreCase(two.getSortName());
1430        }
1431      };
1432
1433
1434  }
1435
1436
1437  /**
1438    *
1439    *
1440    * Musicbrainz Track Class Object<br>
1441    * ~~~~~~~~~~~~~~~~
1442    * representing the returned XML from the mb api query
1443    *<pre>
1444        <?xml version="1.0" encoding="UTF-8"?>
1445
1446     </pre>
1447    *
1448    **/
1449  class MusicbrainzTrack
1450  {
1451  }
1452
1453
1454  /**
1455    *
1456    *
1457    * Musicbrainz Area/region Class Object<br>
1458    * ~~~~~~~~~~~~~~~~
1459    * representing the returned XML from the mb api query
1460    *<pre>
1461        <?xml version="1.0" encoding="UTF-8"?>
1462
1463     </pre>
1464    *
1465    **/
1466  class MusicbrainzArea implements Comparable<MusicbrainzArea>
1467  {
1468    String name = "";
1469    String id = "";
1470    String sortName = "";
1471    String iso31661code = "";
1472
1473    public MusicbrainzArea()
1474    {
1475    }
1476
1477
1478    public MusicbrainzArea(String areaName)
1479    {
1480      name = areaName;
1481      sortName = name;
1482    }
1483
1484
1485    public MusicbrainzArea(nu.xom.Element areaElem )
1486    {
1487      try
1488      {
1489        // parse the area info from the Element
1490        if (areaElem!=null )
1491        {
1492          if(areaElem.getLocalName().equalsIgnoreCase("area") )
1493          {
1494              id=areaElem.getAttributeValue("id");
1495
1496              nu.xom.Elements areaSubElems = areaElem.getChildElements();
1497              if (areaSubElems!=null )
1498              {
1499                nu.xom.Element currElem = null;
1500                for (int ii=0; ii< areaSubElems.size(); ii++)
1501                {
1502                  currElem = areaSubElems.get(ii);
1503                  if(currElem.getLocalName().equalsIgnoreCase("sort-name") ) sortName = currElem.getValue();
1504                  if(currElem.getLocalName().equalsIgnoreCase("name") ) name = currElem.getValue();
1505                }
1506              }
1507          }
1508          else
1509            System.out.print(" !!! "+areaElem.getLocalName());
1510        }
1511      }
1512      catch (Exception ex)
1513      {
1514        ex.printStackTrace();
1515        System.out.println("\nParsing error - Area raw results:");
1516        System.out.println(areaElem);
1517      }
1518    }
1519
1520
1521    public String toString(){return name;}
1522
1523
1524  public String getPropsString()
1525  {
1526    String p = "MusicbrainzArea_";
1527    String retVal = "#MusicBrainz Area Properties\n";
1528    retVal += p+"name="+name+"\n";
1529    retVal += p+"sortName="+getSortName()+"\n";
1530    retVal += p+"id="+id+"\n";
1531    retVal += p+"iso31661code="+iso31661code+"\n";
1532
1533    return retVal;
1534  }
1535
1536
1537
1538  /**
1539    * Set Method for class field 'sortName'.
1540    *
1541    * @param sortName is the value to set this class field to.
1542    *
1543    **/
1544  public  void setSortName(String sortName)
1545  {
1546    this.sortName = sortName;
1547  }  // setSortName Method
1548
1549
1550  /**
1551    * Get Method for class field 'sortName'.
1552    *
1553    * @return String - The value the class field 'sortName'.
1554    *
1555    **/
1556  public String getSortName()
1557  {
1558    return sortName;
1559  }  // getSortName Method
1560
1561
1562    /** Comparator for ignore case sort. **/
1563    public int compareToIgnoreCase(MusicbrainzArea other)
1564    {
1565      return this.getSortName().compareToIgnoreCase(other.getSortName());
1566    }
1567
1568
1569    /** implements Comparator. **/
1570    @Override public int compareTo(MusicbrainzArea other)
1571    {
1572      return this.getSortName().compareTo(other.getSortName());
1573    }
1574
1575
1576    /** A Comparator that can be used to sort Artist vectors. **/
1577    public static Comparator <MusicbrainzArea> ArtistComparator = new Comparator<MusicbrainzArea>()
1578      {
1579        @Override public int compare(MusicbrainzArea one, MusicbrainzArea two)
1580        {
1581          return one.getSortName().compareTo(two.getSortName());
1582        }
1583      };
1584
1585
1586    /** A case in-sensitive Comparator that can be used to sort MusicbrainzArtist vectors. **/
1587    public static Comparator <MusicbrainzArea> AreaComparatorIgnoreCase = new Comparator<MusicbrainzArea>()
1588      {
1589        @Override public int compare(MusicbrainzArea one, MusicbrainzArea two)
1590        {
1591          //return one.name(true).compareTo(two.name(true));
1592          return one.getSortName().compareToIgnoreCase(two.getSortName());
1593        }
1594      };
1595  }
1596
1597
1598  /**
1599    *
1600    *
1601    * Musicbrainz Begin Class Object<br>
1602    * ~~~~~~~~~~~~~~~~
1603    * representing the returned XML from the mb api query
1604    *<pre>
1605        <?xml version="1.0" encoding="UTF-8"?>
1606
1607     </pre>
1608    *
1609    **/
1610  class MusicbrainzBeginArea
1611  {
1612  }
1613
1614
1615  /**
1616    *
1617    *
1618    * Musicbrainz Life-span Class Object<br>
1619    * ~~~~~~~~~~~~~~~~
1620    * representing the returned XML from the mb api query
1621    *<pre>
1622        <?xml version="1.0" encoding="UTF-8"?>
1623
1624     </pre>
1625    *
1626    **/
1627  class MusicbrainzLifespan
1628  {
1629  }