1 /*
2  * This file is part of the LibreOffice project.
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * This file incorporates work covered by the following license notice:
9  *
10  *   Licensed to the Apache Software Foundation (ASF) under one or more
11  *   contributor license agreements. See the NOTICE file distributed
12  *   with this work for additional information regarding copyright
13  *   ownership. The ASF licenses this file to you under the Apache
14  *   License, Version 2.0 (the "License"); you may not use this file
15  *   except in compliance with the License. You may obtain a copy of
16  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
17  */
18 
19 package com.sun.star.script.framework.container;
20 
21 import com.sun.star.container.ElementExistException;
22 import com.sun.star.container.XNameAccess;
23 import com.sun.star.container.XNameContainer;
24 
25 import com.sun.star.io.XInputStream;
26 
27 import com.sun.star.lang.WrappedTargetException;
28 import com.sun.star.lang.XMultiComponentFactory;
29 
30 import com.sun.star.script.framework.io.XInputStreamImpl;
31 import com.sun.star.script.framework.io.XInputStreamWrapper;
32 import com.sun.star.script.framework.log.LogUtils;
33 import com.sun.star.script.framework.provider.PathUtils;
34 
35 import com.sun.star.ucb.XSimpleFileAccess;
36 import com.sun.star.ucb.XSimpleFileAccess2;
37 
38 import com.sun.star.uno.Type;
39 import com.sun.star.uno.UnoRuntime;
40 import com.sun.star.uno.XComponentContext;
41 
42 import com.sun.star.uri.XUriReference;
43 import com.sun.star.uri.XUriReferenceFactory;
44 import com.sun.star.uri.XVndSunStarScriptUrl;
45 
46 import java.io.ByteArrayInputStream;
47 import java.io.ByteArrayOutputStream;
48 import java.io.InputStream;
49 import java.io.UnsupportedEncodingException;
50 
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.StringTokenizer;
54 
55 /**
56  * The <code>ParcelContainer</code> object is used to store the
57  * ScripingFramework specific Libraries.
58  */
59 public class ParcelContainer implements XNameAccess {
60 
61     protected static XSimpleFileAccess m_xSFA;
62 
63     protected String language;
64     protected String containerUrl;
65     private Collection<Parcel> parcels = new ArrayList<Parcel>(10);
66     protected XComponentContext m_xCtx;
67     private ParcelContainer parent = null;
68     private final Collection<ParcelContainer> childContainers = new ArrayList<ParcelContainer>(10);
69     private boolean isPkgContainer = false;
70 
71     /**
72      * Tests if this <tt>ParcelContainer</tt> represents a UNO package
73      * or sub package within a UNO package
74      *
75      * @return    <tt>true</tt> if has parent <tt>false</tt> otherwise
76      */
77     public boolean isUnoPkg() {
78         return isPkgContainer;
79     }
80 
81     /**
82      * Returns this <tt>ParcelContainer</tt>'s  parent
83      *
84      * @return    <tt>ParcelContainer</tt> if has parent null otherwise
85      */
86     public ParcelContainer parent() {
87         return parent;
88     }
89 
90     /**
91      * Returns all child <tt>ParcelContainer</tt>
92      * this instance of <tt>ParcelContainer</tt>
93      *
94      * @return    a new array of ParcelContainers. A zero
95      * length array is returned if no child ParcelContainers.
96      */
97     public ParcelContainer[] getChildContainers() {
98         if (childContainers.isEmpty()) {
99             return new ParcelContainer[0];
100         }
101 
102         return childContainers.toArray(new ParcelContainer[childContainers.size()]);
103     }
104 
105     /**
106      * Removes a child <tt>ParcelContainer</tt>
107      * from this instance.
108      * @param   child  <tt>ParcelContainer</tt> to be added.
109      *
110      * @return    <tt>true</tt> if child successfully removed
111      */
112     public boolean removeChildContainer(ParcelContainer child) {
113         return childContainers.remove(child);
114     }
115 
116     /**
117      * Adds a new child <tt>ParcelContainer</tt>
118      * to this instance.
119      * @param   child  <tt>ParcelContainer</tt> to be added.
120      *
121      */
122     public void addChildContainer(ParcelContainer child) {
123         childContainers.add(child);
124     }
125 
126     /**
127      * Returns a child <tt>ParcelContainer</tt> whose location
128      * matches the <tt>location</tt> argument passed to this method.
129      * @param    key the <tt>location</tt> which is to
130      *           be matched.
131      *
132      * @return    child <tt>ParcelContainer</tt> or {@code null} if none
133      * found.
134      */
135     public ParcelContainer getChildContainer(String key) {
136         ParcelContainer result = null;
137 
138         for (ParcelContainer c : childContainers) {
139 
140             String name = c.getName();
141             if (name == null)
142             {
143                 continue;
144             }
145 
146             String location =
147                 ScriptMetaData.getLocationPlaceHolder(c.containerUrl, name);
148 
149             if (key.equals(location)) {
150                 result = c;
151                 break;
152             }
153         }
154 
155         return result;
156     }
157 
158     /**
159      * Returns a child <tt>ParcelContainer</tt> whose member
160      * <tt>containerUrl</tt> matches the <tt>containerUrl</tt>
161      * argument passed to this method.
162      * @param    containerUrl the <tt>containerUrl</tt> which is to
163      *           be matched.
164      *
165      * @return    child <tt>ParcelContainer</tt> or {@code null} if none
166      * found.
167      */
168     public ParcelContainer getChildContainerForURL(String containerUrl) {
169         ParcelContainer result = null;
170 
171         for (ParcelContainer c : childContainers) {
172             if (containerUrl.equals(c.containerUrl)) {
173                 result = c;
174                 break;
175             }
176         }
177 
178         return result;
179     }
180 
181     /**
182      * Returns Name of this container. Name of this <tt>ParcelContainer</tt>
183      * is determined from the <tt>containerUrl</tt> as the last portion
184      * of the URL after the last forward slash.
185      * @return    name of <tt>ParcelContainer</tt>
186      * found.
187      */
188     public String getName() {
189         String name = null;
190 
191         // TODO handler package ParcelContainer?
192         if (!containerUrl.startsWith("vnd.sun.star.tdoc:")) {
193             try {
194                 // return name
195                 String decodedUrl = java.net.URLDecoder.decode(containerUrl, "UTF-8");
196                 int indexOfSlash = decodedUrl.lastIndexOf('/');
197 
198                 if (indexOfSlash != -1) {
199                     name =  decodedUrl.substring(indexOfSlash + 1);
200                 }
201             } catch (UnsupportedEncodingException e) {
202                 throw new com.sun.star.uno.RuntimeException(e);
203             }
204         } else {
205             name =  "document";
206         }
207 
208         return name;
209     }
210 
211     /**
212      * Initializes a newly created <code>ParcelContainer</code> object.
213      * @param    xCtx UNO component context
214      * @param   containerUrl location of this container.
215      * @param   language language for which entries are stored
216      */
217     public ParcelContainer(XComponentContext xCtx, String containerUrl,
218                            String language) throws
219         com.sun.star.lang.IllegalArgumentException,
220         com.sun.star.lang.WrappedTargetException {
221 
222         this(null, xCtx, containerUrl, language, true);
223     }
224 
225     /**
226      * Initializes a newly created <code>ParcelContainer</code> object.
227      * @param    xCtx UNO component context
228      * @param   containerUrl location of this container.
229      * @param   language language for which entries are stored
230      * @param   loadParcels set to <tt>true</tt> if parcels are to be loaded
231      *          on construction.
232      */
233     public ParcelContainer(XComponentContext xCtx, String containerUrl,
234                            String language, boolean loadParcels) throws
235         com.sun.star.lang.IllegalArgumentException,
236         com.sun.star.lang.WrappedTargetException {
237 
238         this(null, xCtx, containerUrl, language, loadParcels);
239     }
240 
241     /**
242      * Initializes a newly created <code>ParcelContainer</code> object.
243      * @param   parent parent ParcelContainer
244      * @param    xCtx UNO component context
245      * @param   containerUrl location of this container.
246      * @param   language language for which entries are stored
247      * @param   loadParcels set to <tt>true</tt> if parcels are to be loaded
248      *          on construction.
249      */
250     public ParcelContainer(ParcelContainer parent, XComponentContext xCtx,
251                            String containerUrl, String language,
252                            boolean loadParcels) throws
253         com.sun.star.lang.IllegalArgumentException,
254         com.sun.star.lang.WrappedTargetException {
255 
256         LogUtils.DEBUG("Creating ParcelContainer for " + containerUrl +
257                        " loadParcels = " + loadParcels + " language = " + language);
258 
259         this.m_xCtx = xCtx;
260         this.language = language;
261         this.parent = parent;
262         this.containerUrl = containerUrl;
263 
264         initSimpleFileAccess();
265         boolean parentIsPkgContainer = false;
266 
267         if (parent != null) {
268             parentIsPkgContainer = parent.isUnoPkg();
269         }
270 
271         if (containerUrl.endsWith("uno_packages") || parentIsPkgContainer) {
272             isPkgContainer = true;
273         }
274 
275         if (loadParcels) {
276             loadParcels();
277         }
278     }
279 
280 
281     public String getContainerURL() {
282         return this.containerUrl;
283     }
284 
285     private void initSimpleFileAccess() {
286         synchronized (ParcelContainer.class) {
287             if (m_xSFA != null) {
288                 return;
289             }
290 
291             try {
292 
293                 m_xSFA = UnoRuntime.queryInterface(
294                              XSimpleFileAccess.class,
295                              m_xCtx.getServiceManager().createInstanceWithContext(
296                                  "com.sun.star.ucb.SimpleFileAccess", m_xCtx));
297 
298             } catch (Exception e) {
299                 // TODO should throw
300                 LogUtils.DEBUG("Error instantiating simplefile access ");
301                 LogUtils.DEBUG(LogUtils.getTrace(e));
302             }
303         }
304     }
305 
306     public String getParcelContainerDir() {
307         // If this container does not represent a uno-package
308         // then it is a document, user or share
309         // in each case the convention is to have a Scripts/[language]
310         // dir where scripts reside
311         if (!isUnoPkg()) {
312             return PathUtils.make_url(containerUrl  ,  "Scripts/" + language.toLowerCase());
313         }
314 
315         return containerUrl;
316     }
317 
318     public Object getByName(String aName) throws
319         com.sun.star.container.NoSuchElementException, WrappedTargetException {
320 
321         Parcel parcel = null;
322 
323         try {
324             if (hasElements()) {
325                 for (Parcel parcelToCheck : parcels) {
326                     if (parcelToCheck.getName().equals(aName)) {
327                         parcel = parcelToCheck;
328                         break;
329                     }
330                 }
331             }
332         } catch (Exception e) {
333             throw new WrappedTargetException(e);
334         }
335 
336         if (parcel == null) {
337             throw new com.sun.star.container.NoSuchElementException("Macro Library " + aName
338                     + " not found");
339         }
340 
341         return parcel;
342     }
343 
344     public String[] getElementNames() {
345 
346         if (hasElements()) {
347 
348             Parcel[] theParcels = parcels.toArray(new Parcel[parcels.size()]);
349             String[] names = new String[ theParcels.length ];
350 
351             for (int count = 0; count < names.length; count++) {
352                 names[count] = theParcels[ count ].getName();
353             }
354 
355             return names;
356         }
357 
358         return new String[0];
359     }
360 
361     public boolean hasByName(String aName) {
362 
363         boolean isFound = false;
364 
365         try {
366             if (getByName(aName) != null) {
367                 isFound = true;
368             }
369 
370         } catch (Exception e) {
371             //TODO - handle trace
372         }
373 
374         return isFound;
375     }
376 
377     public Type getElementType() {
378         return new Type();
379     }
380 
381     public boolean hasElements() {
382         return !(parcels == null || parcels.isEmpty());
383     }
384 
385     private void loadParcels() throws com.sun.star.lang.IllegalArgumentException,
386         com.sun.star.lang.WrappedTargetException {
387 
388         try {
389             LogUtils.DEBUG("About to load parcels from " + containerUrl);
390 
391             if (m_xSFA.isFolder(getParcelContainerDir())) {
392                 LogUtils.DEBUG(getParcelContainerDir() + " is a folder ");
393                 String[] children = m_xSFA.getFolderContents(getParcelContainerDir(), true);
394                 parcels  = new ArrayList<Parcel>(children.length);
395 
396                 for (String child : children) {
397                     LogUtils.DEBUG("Processing " + child);
398 
399                     try {
400                         loadParcel(child);
401                     } catch (java.lang.Exception e) {
402                         // print an error message and move on to
403                         // the next parcel
404                         LogUtils.DEBUG("ParcelContainer.loadParcels caught " + e.getClass().getName() +
405                                        " exception loading parcel " + child + ": " + e.getMessage());
406                     }
407                 }
408             } else {
409                 LogUtils.DEBUG(" ParcelCOntainer.loadParcels " + getParcelContainerDir()  +
410                                " is not a folder ");
411             }
412 
413         } catch (com.sun.star.ucb.CommandAbortedException e) {
414             LogUtils.DEBUG("ParcelContainer.loadParcels caught exception processing folders for "
415                            + getParcelContainerDir());
416             LogUtils.DEBUG("TRACE: " + LogUtils.getTrace(e));
417             throw new com.sun.star.lang.WrappedTargetException(e);
418         } catch (com.sun.star.uno.Exception e) {
419             LogUtils.DEBUG("ParcelContainer.loadParcels caught exception processing folders for "
420                            + getParcelContainerDir());
421             LogUtils.DEBUG("TRACE: " + LogUtils.getTrace(e));
422             throw new com.sun.star.lang.WrappedTargetException(e);
423         }
424     }
425 
426     public  XNameContainer createParcel(String name) throws
427         ElementExistException, com.sun.star.lang.WrappedTargetException {
428 
429         Parcel p = null;
430 
431         if (hasByName(name)) {
432             throw new ElementExistException("Parcel " + name + " already exists");
433         }
434 
435         String pathToParcel = PathUtils.make_url(getParcelContainerDir(), name);
436 
437         try {
438             LogUtils.DEBUG("ParcelContainer.createParcel, creating folder "
439                            + pathToParcel);
440 
441             m_xSFA.createFolder(pathToParcel);
442 
443             LogUtils.DEBUG("ParcelContainer.createParcel, folder " + pathToParcel  +
444                            " created ");
445 
446             ParcelDescriptor pd = new ParcelDescriptor();
447             pd.setLanguage(language);
448 
449             String parcelDesc =
450                 PathUtils.make_url(pathToParcel, ParcelDescriptor.PARCEL_DESCRIPTOR_NAME);
451 
452             XSimpleFileAccess2 xSFA2 =
453                 UnoRuntime.queryInterface(XSimpleFileAccess2.class, m_xSFA);
454 
455             if (xSFA2 != null) {
456                 LogUtils.DEBUG("createParcel() Using XSIMPLEFILEACCESS2 " + parcelDesc);
457                 ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
458                 pd.write(bos);
459                 bos.close();
460                 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
461                 XInputStreamImpl xis = new XInputStreamImpl(bis);
462                 xSFA2.writeFile(parcelDesc, xis);
463                 xis.closeInput();
464                 p = loadParcel(pathToParcel);
465             }
466         } catch (Exception e) {
467             LogUtils.DEBUG("createParcel() Exception while attempting to create = "
468                            + name);
469             throw new com.sun.star.lang.WrappedTargetException(e);
470         }
471 
472         return p;
473     }
474 
475     public Parcel loadParcel(String parcelUrl) throws
476         com.sun.star.lang.WrappedTargetException,
477         com.sun.star.lang.IllegalArgumentException {
478 
479         String parcelDescUrl =
480             PathUtils.make_url(parcelUrl, ParcelDescriptor.PARCEL_DESCRIPTOR_NAME);
481 
482         Parcel parcel = null;
483         XInputStream xis = null;
484         InputStream is = null;
485 
486         try {
487             if (m_xSFA.exists(parcelDescUrl)) {
488 
489                 LogUtils.DEBUG("ParcelContainer.loadParcel opening " + parcelDescUrl);
490 
491                 xis = m_xSFA.openFileRead(parcelDescUrl);
492                 is = new XInputStreamWrapper(xis);
493                 ParcelDescriptor pd = new ParcelDescriptor(is) ;
494 
495                 try {
496                     is.close();
497                     is = null;
498                 } catch (Exception e) {
499                     LogUtils.DEBUG(
500                         "ParcelContainer.loadParcel Exception when closing stream for  "
501                         + parcelDescUrl + " :" + e);
502                 }
503 
504                 LogUtils.DEBUG("ParcelContainer.loadParcel closed " + parcelDescUrl);
505 
506                 if (!pd.getLanguage().equals(language)) {
507                     LogUtils.DEBUG("ParcelContainer.loadParcel Language of Parcel does not match this container ");
508                     return null;
509                 }
510 
511                 LogUtils.DEBUG("Processing " + parcelDescUrl + " closed ");
512 
513                 int indexOfSlash = parcelUrl.lastIndexOf('/');
514                 String name = parcelUrl.substring(indexOfSlash + 1);
515 
516                 parcel = new Parcel(m_xSFA, this, pd, name);
517 
518                 LogUtils.DEBUG(" ParcelContainer.loadParcel created parcel for "
519                                + parcelDescUrl + " for language " + language);
520 
521                 parcels.add(parcel);
522             } else {
523                 throw new java.io.IOException(parcelDescUrl + " does NOT exist!");
524             }
525         } catch (com.sun.star.ucb.CommandAbortedException e) {
526             LogUtils.DEBUG("loadParcel() Exception while accessing filesystem url = "
527                            + parcelDescUrl + e);
528             throw new com.sun.star.lang.WrappedTargetException(e);
529         } catch (java.io.IOException e) {
530             LogUtils.DEBUG("ParcelContainer.loadParcel() caught IOException while accessing "
531                            + parcelDescUrl + ": " + e);
532             throw new com.sun.star.lang.WrappedTargetException(e);
533         } catch (com.sun.star.uno.Exception e) {
534             LogUtils.DEBUG("loadParcel() Exception while accessing filesystem url = "
535                            + parcelDescUrl + e);
536             throw new com.sun.star.lang.WrappedTargetException(e);
537         }
538 
539         finally {
540             if (is != null) {
541                 try {
542                     is.close(); // is will close xis
543                 } catch (Exception ignore) {
544                 }
545             } else if (xis != null) {
546                 try {
547                     xis.closeInput();
548                 } catch (Exception ignore) {
549                 }
550             }
551         }
552 
553         return parcel;
554     }
555     public void renameParcel(String oldName, String newName) throws
556         com.sun.star.container.NoSuchElementException,
557         com.sun.star.lang.WrappedTargetException {
558 
559         LogUtils.DEBUG(" ** ParcelContainer Renaming parcel " + oldName + " to " +
560                        newName);
561         LogUtils.DEBUG(" ** ParcelContainer is " + this);
562 
563         Parcel p = (Parcel)getByName(oldName);
564 
565         if (p == null) {
566             throw new com.sun.star.container.NoSuchElementException(
567                 "No parcel named " + oldName);
568         }
569 
570         String oldParcelDirUrl =
571             PathUtils.make_url(getParcelContainerDir(), oldName);
572 
573         String newParcelDirUrl =
574             PathUtils.make_url(getParcelContainerDir(), newName);
575 
576         try {
577             if (!m_xSFA.isFolder(oldParcelDirUrl)) {
578                 Exception e = new com.sun.star.io.IOException(
579                     "Invalid Parcel directory: " + oldName);
580                 throw new com.sun.star.lang.WrappedTargetException(e);
581             }
582 
583             LogUtils.DEBUG(" ** ParcelContainer Renaming folder " + oldParcelDirUrl
584                            + " to " + newParcelDirUrl);
585 
586             m_xSFA.move(oldParcelDirUrl, newParcelDirUrl);
587 
588         } catch (com.sun.star.ucb.CommandAbortedException ce) {
589             LogUtils.DEBUG(" ** ParcelContainer Renaming failed with " + ce);
590             throw new com.sun.star.lang.WrappedTargetException(ce);
591         } catch (com.sun.star.uno.Exception e) {
592             LogUtils.DEBUG(" ** ParcelContainer Renaming failed with " + e);
593             throw new com.sun.star.lang.WrappedTargetException(e);
594         }
595 
596         p.rename(newName);
597     }
598     // removes but doesn't physically delete parcel from container
599     public boolean removeParcel(String name) throws
600         com.sun.star.container.NoSuchElementException,
601         com.sun.star.lang.WrappedTargetException {
602 
603         Parcel p = (Parcel)getByName(name);
604 
605         if (p == null) {
606             throw new com.sun.star.container.NoSuchElementException(
607                 "No parcel named " + name);
608         }
609 
610         return  parcels.remove(p);
611     }
612 
613     public boolean deleteParcel(String name) throws
614         com.sun.star.container.NoSuchElementException,
615         com.sun.star.lang.WrappedTargetException {
616 
617         LogUtils.DEBUG("deleteParcel for containerURL " + containerUrl
618                        + " name = " + name  + " Language = " + language);
619 
620         Parcel p = (Parcel)getByName(name);
621 
622         if (p == null) {
623             throw new com.sun.star.container.NoSuchElementException(
624                 "No parcel named " + name);
625         }
626 
627         try {
628             String pathToParcel = PathUtils.make_url(getParcelContainerDir(), name);
629             m_xSFA.kill(pathToParcel);
630         } catch (Exception e) {
631             LogUtils.DEBUG("Error deleting parcel " + name);
632             throw new com.sun.star.lang.WrappedTargetException(e);
633         }
634 
635         return  parcels.remove(p);
636     }
637 
638     public String getLanguage() {
639         return language;
640     }
641 
642     public ScriptMetaData findScript(ParsedScriptUri  parsedUri) throws
643         com.sun.star.container.NoSuchElementException,
644         com.sun.star.lang.WrappedTargetException {
645 
646         Parcel p = (Parcel)getByName(parsedUri.parcel);
647         ScriptMetaData scriptData = p.getByName(parsedUri.function);
648 
649         LogUtils.DEBUG("** found script data for " +  parsedUri.function + " script is "
650                        + scriptData);
651 
652         return scriptData;
653     }
654 
655     public  ParsedScriptUri parseScriptUri(String scriptURI) throws
656         com.sun.star.lang.IllegalArgumentException {
657 
658         XMultiComponentFactory xMcFac = null;
659         XUriReferenceFactory xFac = null;
660 
661         try {
662             xMcFac = m_xCtx.getServiceManager();
663 
664             xFac = UnoRuntime.queryInterface(
665                        XUriReferenceFactory.class, xMcFac.createInstanceWithContext(
666                            "com.sun.star.uri.UriReferenceFactory", m_xCtx));
667 
668         } catch (com.sun.star.uno.Exception e) {
669             LogUtils.DEBUG("Problems parsing  URL:" + e.toString());
670             throw new com.sun.star.lang.IllegalArgumentException(
671                 e, "Problems parsing URL");
672         }
673 
674         if (xFac == null) {
675             LogUtils.DEBUG("Failed to create UrlReference factory");
676             throw new com.sun.star.lang.IllegalArgumentException(
677                 "Failed to create UrlReference factory for url " + scriptURI);
678         }
679 
680         XUriReference uriRef = xFac.parse(scriptURI);
681 
682         XVndSunStarScriptUrl sfUri =
683             UnoRuntime.queryInterface(XVndSunStarScriptUrl.class, uriRef);
684 
685         if (sfUri == null) {
686             LogUtils.DEBUG("Failed to parse url");
687             throw new com.sun.star.lang.IllegalArgumentException(
688                 "Failed to parse url " + scriptURI);
689         }
690 
691         ParsedScriptUri parsedUri = new ParsedScriptUri();
692         parsedUri.function = sfUri.getName();
693         parsedUri.parcel = "";
694 
695         // parse parcel name;
696         StringTokenizer tokenizer = new StringTokenizer(parsedUri.function, ".");
697 
698         if (tokenizer.hasMoreElements()) {
699             parsedUri.parcel = (String)tokenizer.nextElement();
700             LogUtils.DEBUG("** parcelName = " + parsedUri.parcel);
701         }
702 
703         if (parsedUri.function.length() > 0) {
704 
705             // strip out parcel name
706             parsedUri.function =
707                 parsedUri.function.substring(parsedUri.parcel.length() + 1);
708 
709         }
710 
711         // parse location
712         parsedUri.location = sfUri.getParameter("location");
713 
714         // TODO basic sanity check on language, location, function name, parcel
715         // should be correct e.g. verified by MSP and LangProvider by the
716         // time it's got to here
717 
718         LogUtils.DEBUG("** location = " + parsedUri.location +
719                        "\nfunction = " + parsedUri.function +
720                        "\nparcel = " + parsedUri.parcel +
721                        "\nlocation = " + parsedUri.location);
722 
723         return parsedUri;
724     }
725 }
726