/** Check if the getSubBuilders properly predicts the output. */
  public static void testSubBuilders() throws Exception {
    Workspace ws = Workspace.getWorkspace(new File("test/ws"));
    Project project = ws.getProject("p4-sub");

    Collection<? extends Builder> bs = project.getSubBuilders();
    assertNotNull(bs);
    assertEquals(3, bs.size());
    Set<String> names = new HashSet<String>();
    for (Builder b : bs) {
      names.add(b.getBsn());
    }
    assertTrue(names.contains("p4-sub.a"));
    assertTrue(names.contains("p4-sub.b"));
    assertTrue(names.contains("p4-sub.c"));

    File[] files = project.build();
    assertTrue(project.check());

    System.err.println(Processor.join(project.getErrors(), "\n"));
    System.err.println(Processor.join(project.getWarnings(), "\n"));
    assertEquals(0, project.getErrors().size());
    assertEquals(0, project.getWarnings().size());
    assertNotNull(files);
    assertEquals(3, files.length);
    for (File file : files) {
      Jar jar = new Jar(file);
      Manifest m = jar.getManifest();
      assertTrue(names.contains(m.getMainAttributes().getValue("Bundle-SymbolicName")));
    }
  }
Exemple #2
0
 public Set<String> listResources(String subdir) {
   try {
     Set<String> result = new HashSet<String>();
     if (resourceURL != null) {
       String protocol = resourceURL.getProtocol();
       if (protocol.equals("jar")) {
         String resPath = resourceURL.getPath();
         int pling = resPath.lastIndexOf("!");
         URL jarURL = new URL(resPath.substring(0, pling));
         String resDirInJar = resPath.substring(pling + 2);
         String prefix = resDirInJar + subdir + "/";
         // System.out.printf("BaseMod.listResources: looking for names starting with %s\n",
         // prefix);
         JarFile jar = new JarFile(new File(jarURL.toURI()));
         Enumeration<JarEntry> entries = jar.entries();
         while (entries.hasMoreElements()) {
           String name = entries.nextElement().getName();
           if (name.startsWith(prefix) && !name.endsWith("/") && !name.contains("/.")) {
             // System.out.printf("BaseMod.listResources: name = %s\n", name);
             result.add(name.substring(prefix.length()));
           }
         }
       } else throw new RuntimeException("Resource URL protocol " + protocol + " not supported");
     }
     return result;
   } catch (Exception e) {
     throw new RuntimeException(e);
   }
 }
Exemple #3
0
 /**
  * Expands list of files to process into full list of all files that can be found by recursively
  * descending directories.
  */
 void expand(File dir, String[] files, boolean isUpdate) {
   if (files == null) {
     return;
   }
   for (int i = 0; i < files.length; i++) {
     File f;
     if (dir == null) {
       f = new File(files[i]);
     } else {
       f = new File(dir, files[i]);
     }
     if (f.isFile()) {
       if (entries.add(f)) {
         if (isUpdate) entryMap.put(entryName(f.getPath()), f);
       }
     } else if (f.isDirectory()) {
       if (entries.add(f)) {
         if (isUpdate) {
           String dirPath = f.getPath();
           dirPath = (dirPath.endsWith(File.separator)) ? dirPath : (dirPath + File.separator);
           entryMap.put(entryName(dirPath), f);
         }
         expand(f, f.list(), isUpdate);
       }
     } else {
       error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
       ok = false;
     }
   }
 }
  private void checkStartup(
      Map<String, ServiceData> map,
      List<ServiceData> start,
      ServiceData sd,
      Set<ServiceData> cyclic) {
    if (sd.after.isEmpty() || start.contains(sd)) return;

    if (cyclic.contains(sd)) {
      reporter.error("Cyclic dependency for " + sd.name);
      return;
    }

    cyclic.add(sd);

    for (String dependsOn : sd.after) {
      if (dependsOn.equals("boot")) continue;

      ServiceData deps = map.get(dependsOn);
      if (deps == null) {
        reporter.error("No such service " + dependsOn + " but " + sd.name + " depends on it");
      } else {
        checkStartup(map, start, deps, cyclic);
      }
    }
    start.add(sd);
  }
  private static void check(String what, MBeanNotificationInfo[] mbnis) {
    System.out.print(what + ": checking notification info: ");

    if (mbnis.length == 0) {
      System.out.println("NONE (suspicious)");
      suspicious.add(what);
      return;
    }

    // Each MBeanNotificationInfo.getName() should be an existent
    // Java class that is Notification or a subclass of it
    for (int j = 0; j < mbnis.length; j++) {
      String notifClassName = mbnis[j].getName();
      Class notifClass;
      try {
        notifClass = Class.forName(notifClassName);
      } catch (Exception e) {
        System.out.print("FAILED(" + notifClassName + ": " + e + ") ");
        failed.add(what);
        continue;
      }
      if (!Notification.class.isAssignableFrom(notifClass)) {
        System.out.print("FAILED(" + notifClassName + ": not a Notification) ");
        failed.add(what);
        continue;
      }
      System.out.print("OK(" + notifClassName + ") ");
    }
    System.out.println();
  }
Exemple #6
0
  /** Extracts specified entries from JAR file. */
  void extract(InputStream in, String files[]) throws IOException {
    ZipInputStream zis = new ZipInputStream(in);
    ZipEntry e;
    // Set of all directory entries specified in archive.  Disallows
    // null entries.  Disallows all entries if using pre-6.0 behavior.
    Set<ZipEntry> dirs = newDirSet();
    while ((e = zis.getNextEntry()) != null) {
      if (files == null) {
        dirs.add(extractFile(zis, e));
      } else {
        String name = e.getName();
        for (String file : files) {
          if (name.startsWith(file)) {
            dirs.add(extractFile(zis, e));
            break;
          }
        }
      }
    }

    // Update timestamps of directories specified in archive with their
    // timestamps as given in the archive.  We do this after extraction,
    // instead of during, because creating a file in a directory changes
    // that directory's timestamp.
    updateLastModifiedTime(dirs);
  }
 private static Set<String> findStandardMBeansFromJar(URL codeBase) throws Exception {
   InputStream is = codeBase.openStream();
   JarInputStream jis = new JarInputStream(is);
   Set<String> names = new TreeSet<String>();
   JarEntry entry;
   while ((entry = jis.getNextJarEntry()) != null) {
     String name = entry.getName();
     if (!name.endsWith(".class")) continue;
     name = name.substring(0, name.length() - 6);
     name = name.replace('/', '.');
     names.add(name);
   }
   return names;
 }
Exemple #8
0
  /**
   * Enumerates the resouces in a give package name. This works even if the resources are loaded
   * from a jar file!
   *
   * <p>Adapted from code by mikewse on the java.sun.com message boards.
   * http://forum.java.sun.com/thread.jsp?forum=22&thread=30984
   *
   * @param packageName The package to enumerate
   * @return A Set of Strings for each resouce in the package.
   */
  public static Set getResoucesInPackage(String packageName) throws IOException {
    String localPackageName;
    if (packageName.endsWith("/")) {
      localPackageName = packageName;
    } else {
      localPackageName = packageName + '/';
    }

    Enumeration dirEnum = ClassLoader.getSystemResources(localPackageName);

    Set names = new HashSet();

    // Loop CLASSPATH directories
    while (dirEnum.hasMoreElements()) {
      URL resUrl = (URL) dirEnum.nextElement();

      // Pointing to filesystem directory
      if (resUrl.getProtocol().equals("file")) {
        File dir = new File(resUrl.getFile());
        File[] files = dir.listFiles();
        if (files != null) {
          for (int i = 0; i < files.length; i++) {
            File file = files[i];
            if (file.isDirectory()) continue;
            names.add(localPackageName + file.getName());
          }
        }

        // Pointing to Jar file
      } else if (resUrl.getProtocol().equals("jar")) {
        JarURLConnection jconn = (JarURLConnection) resUrl.openConnection();
        JarFile jfile = jconn.getJarFile();
        Enumeration entryEnum = jfile.entries();
        while (entryEnum.hasMoreElements()) {
          JarEntry entry = (JarEntry) entryEnum.nextElement();
          String entryName = entry.getName();
          // Exclude our own directory
          if (entryName.equals(localPackageName)) continue;
          String parentDirName = entryName.substring(0, entryName.lastIndexOf('/') + 1);
          if (!parentDirName.equals(localPackageName)) continue;
          names.add(entryName);
        }
      } else {
        // Invalid classpath entry
      }
    }

    return names;
  }
  private static String[] findStandardMBeans(URL codeBase) throws Exception {
    Set<String> names;
    if (codeBase.getProtocol().equalsIgnoreCase("file") && codeBase.toString().endsWith("/"))
      names = findStandardMBeansFromDir(codeBase);
    else names = findStandardMBeansFromJar(codeBase);

    Set<String> standardMBeanNames = new TreeSet<String>();
    for (String name : names) {
      if (name.endsWith("MBean")) {
        String prefix = name.substring(0, name.length() - 5);
        if (names.contains(prefix)) standardMBeanNames.add(prefix);
      }
    }
    return standardMBeanNames.toArray(new String[0]);
  }
Exemple #10
0
 /** Extracts specified entries from JAR file, via ZipFile. */
 void extract(String fname, String files[]) throws IOException {
   ZipFile zf = new ZipFile(fname);
   Set<ZipEntry> dirs = newDirSet();
   Enumeration<? extends ZipEntry> zes = zf.entries();
   while (zes.hasMoreElements()) {
     ZipEntry e = zes.nextElement();
     InputStream is;
     if (files == null) {
       dirs.add(extractFile(zf.getInputStream(e), e));
     } else {
       String name = e.getName();
       for (String file : files) {
         if (name.startsWith(file)) {
           dirs.add(extractFile(zf.getInputStream(e), e));
           break;
         }
       }
     }
   }
   zf.close();
   updateLastModifiedTime(dirs);
 }
 private static void scanDir(File dir, String prefix, Set<String> names) throws Exception {
   File[] files = dir.listFiles();
   if (files == null) return;
   for (int i = 0; i < files.length; i++) {
     File f = files[i];
     String name = f.getName();
     String p = (prefix.equals("")) ? name : prefix + "." + name;
     if (f.isDirectory()) scanDir(f, p, names);
     else if (name.endsWith(".class")) {
       p = p.substring(0, p.length() - 6);
       names.add(p);
     }
   }
 }
Exemple #12
0
 private static void completePackage(Set seenClasses, JarFile jar, String packageName) {
   int len = packageName.length();
   Enumeration entries = jar.entries();
   while (entries.hasMoreElements()) {
     JarEntry entry = (JarEntry) entries.nextElement();
     String name = entry.getName();
     if (name.startsWith(packageName) && name.endsWith(".class") && name.lastIndexOf('/') == len) {
       // Trim ".class" from end
       name = name.substring(0, name.length() - 6);
       if (seenClasses.add(name)) {
         System.out.println(name);
       }
     }
   }
 }
Exemple #13
0
  public static void main(String[] args) throws IOException {
    List /*<String>*/ classes = new ArrayList();
    String origJavaHome = System.getProperty("java.home");
    String javaHome = origJavaHome.toLowerCase();
    if (javaHome.endsWith("jre")) {
      origJavaHome = origJavaHome.substring(0, origJavaHome.length() - 4);
      javaHome = javaHome.substring(0, javaHome.length() - 4);
    }
    for (int i = 0; i < args.length; i++) {
      try {
        File file = new File(args[i]);
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line = null;
        while ((line = reader.readLine()) != null) {
          StringTokenizer tok = new StringTokenizer(line, "[ \t\n\r\f");
          if (tok.hasMoreTokens()) {
            String t = tok.nextToken();
            // Understand only "Loading" from -XX:+TraceClassLoadingPreorder.
            // This ignores old "Loaded" from -verbose:class to force correct
            // classlist generation on Mustang.
            if (t.equals("Loading")) {
              t = tok.nextToken();
              t = t.replace('.', '/');

              // Check to make sure it came from the boot class path
              if (tok.hasMoreTokens()) {
                String tmp = tok.nextToken();
                if (tmp.equals("from")) {
                  if (tok.hasMoreTokens()) {
                    tmp = tok.nextToken().toLowerCase();
                    // System.err.println("Loaded " + t + " from " + tmp);
                    if (tmp.startsWith(javaHome)) {
                      // OK, remember this class for later
                      classes.add(t);
                    }
                  }
                }
              }
            }
          }
        }
      } catch (IOException e) {
        System.err.println("Error reading file " + args[i]);
        throw (e);
      }
    }

    Set /*<String>*/ seenClasses = new HashSet();

    for (Iterator iter = classes.iterator(); iter.hasNext(); ) {
      String str = (String) iter.next();
      if (seenClasses.add(str)) {
        System.out.println(str);
      }
    }

    // Try to complete certain packages
    // Note: not using this new code yet; need to consider whether the
    // footprint increase is worth any startup gains
    // Note also that the packages considered below for completion are
    // (obviously) platform-specific
    // JarFile rtJar = new JarFile(origJavaHome + File.separator +
    //                             "jre" + File.separator +
    //                             "lib" + File.separator +
    //                             "rt.jar");
    // completePackage(seenClasses, rtJar, "java/awt");
    // completePackage(seenClasses, rtJar, "sun/awt");
    // completePackage(seenClasses, rtJar, "sun/awt/X11");
    // completePackage(seenClasses, rtJar, "java/awt/im/spi");
    // completePackage(seenClasses, rtJar, "java/lang");
  }
  public void run() throws Exception {
    // Selection of files to be compiled
    File absJar = createJar(new File("abs.jar").getAbsoluteFile(), "j.A");
    File relJar = createJar(new File("rel.jar"), "j.R");
    File absDir = createDir(new File("abs.dir").getAbsoluteFile(), "d.A");
    File relDir = createDir(new File("rel.dir"), "d.R");
    File absTestFile =
        writeFile(new File("AbsTest.java").getAbsoluteFile(), "class AbsTest { class Inner { } }");
    File relTestFile = writeFile(new File("RelTest.java"), "class RelTest { class Inner { } }");
    File relTest2File =
        writeFile(new File("p/RelTest2.java"), "package p; class RelTest2 { class Inner { } }");
    // This next class references other classes that will be found on the source path
    // and which will therefore need to be compiled as well.
    File mainFile =
        writeFile(new File("Main.java"), "class Main { j.A ja; j.R jr; d.A da; d.R dr; }" + "");

    String sourcePath = createPath(absJar, relJar, absDir, relDir);
    File outDir = new File("classes");
    outDir.mkdirs();

    String[] args = {
      "-sourcepath",
      sourcePath,
      "-d",
      outDir.getPath(),
      absTestFile.getPath(),
      relTestFile.getPath(),
      relTest2File.getPath(),
      mainFile.getPath(),
    };
    System.err.println("compile: " + Arrays.asList(args));
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    int rc = com.sun.tools.javac.Main.compile(args, pw);
    pw.close();
    if (rc != 0) {
      System.err.println(sw.toString());
      throw new Exception("unexpected exit from javac: " + rc);
    }

    Set<File> expect =
        getFiles(
            outDir,
            "d/A.class",
            "d/A$Inner.class",
            "d/R.class",
            "d/R$Inner.class",
            "j/A.class",
            "j/A$Inner.class",
            "j/R.class",
            "j/R$Inner.class",
            "AbsTest.class",
            "AbsTest$Inner.class",
            "RelTest.class",
            "RelTest$Inner.class",
            "p/RelTest2.class",
            "p/RelTest2$Inner.class",
            "Main.class");

    Set<File> found = findFiles(outDir);

    if (!found.equals(expect)) {
      if (found.containsAll(expect))
        throw new Exception("unexpected files found: " + diff(found, expect));
      else if (expect.containsAll(found))
        throw new Exception("expected files not found: " + diff(expect, found));
    }

    for (File f : found) verifySourceFileAttribute(f);

    if (errors > 0) throw new Exception(errors + " errors occurred");
  }
 /** Return the difference of two sets, a - b. */
 <T> Set<T> diff(Set<T> a, Set<T> b) {
   if (b.isEmpty()) return a;
   Set<T> result = new LinkedHashSet<T>(a);
   result.removeAll(b);
   return result;
 }
 // where
 void findFiles(File dir, Set<File> files) {
   for (File f : dir.listFiles()) {
     if (f.isDirectory()) findFiles(f, files);
     else files.add(f);
   }
 }
 /** Create a set of files from a base directory and a set of relative paths. */
 Set<File> getFiles(File dir, String... paths) {
   Set<File> files = new LinkedHashSet<File>();
   for (String p : paths) files.add(new File(dir, p));
   return files;
 }
Exemple #18
0
  /** Updates an existing jar file. */
  boolean update(InputStream in, OutputStream out, InputStream newManifest, JarIndex jarIndex)
      throws IOException {
    ZipInputStream zis = new ZipInputStream(in);
    ZipOutputStream zos = new JarOutputStream(out);
    ZipEntry e = null;
    boolean foundManifest = false;
    boolean updateOk = true;

    if (jarIndex != null) {
      addIndex(jarIndex, zos);
    }

    // put the old entries first, replace if necessary
    while ((e = zis.getNextEntry()) != null) {
      String name = e.getName();

      boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME);

      if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) || (Mflag && isManifestEntry)) {
        continue;
      } else if (isManifestEntry && ((newManifest != null) || (ename != null) || (pname != null))) {
        foundManifest = true;
        if (newManifest != null) {
          // Don't read from the newManifest InputStream, as we
          // might need it below, and we can't re-read the same data
          // twice.
          FileInputStream fis = new FileInputStream(mname);
          boolean ambiguous = isAmbiguousMainClass(new Manifest(fis));
          fis.close();
          if (ambiguous) {
            return false;
          }
        }

        // Update the manifest.
        Manifest old = new Manifest(zis);
        if (newManifest != null) {
          old.read(newManifest);
        }
        if (!updateManifest(old, zos)) {
          return false;
        }
      } else {
        if (!entryMap.containsKey(name)) { // copy the old stuff
          // do our own compression
          ZipEntry e2 = new ZipEntry(name);
          e2.setMethod(e.getMethod());
          e2.setTime(e.getTime());
          e2.setComment(e.getComment());
          e2.setExtra(e.getExtra());
          if (e.getMethod() == ZipEntry.STORED) {
            e2.setSize(e.getSize());
            e2.setCrc(e.getCrc());
          }
          zos.putNextEntry(e2);
          copy(zis, zos);
        } else { // replace with the new files
          File f = entryMap.get(name);
          addFile(zos, f);
          entryMap.remove(name);
          entries.remove(f);
        }
      }
    }

    // add the remaining new files
    for (File f : entries) {
      addFile(zos, f);
    }
    if (!foundManifest) {
      if (newManifest != null) {
        Manifest m = new Manifest(newManifest);
        updateOk = !isAmbiguousMainClass(m);
        if (updateOk) {
          if (!updateManifest(m, zos)) {
            updateOk = false;
          }
        }
      } else if (ename != null || pname != null) {
        if (!updateManifest(new Manifest(), zos)) {
          updateOk = false;
        }
      }
    }
    zis.close();
    zos.close();
    return updateOk;
  }
  public static void main(String[] args) throws Exception {
    System.out.println(
        "Checking that all known MBeans that are "
            + "NotificationBroadcasters have sane "
            + "MBeanInfo.getNotifications()");

    System.out.println("Checking platform MBeans...");
    checkPlatformMBeans();

    URL codeBase = ClassLoader.getSystemResource("javax/management/MBeanServer.class");
    if (codeBase == null) {
      throw new Exception("Could not determine codeBase for " + MBeanServer.class);
    }

    System.out.println();
    System.out.println("Looking for standard MBeans...");
    String[] classes = findStandardMBeans(codeBase);

    System.out.println("Testing standard MBeans...");
    for (int i = 0; i < classes.length; i++) {
      String name = classes[i];
      Class<?> c;
      try {
        c = Class.forName(name);
      } catch (Throwable e) {
        System.out.println(name + ": cannot load (not public?): " + e);
        continue;
      }
      if (!NotificationBroadcaster.class.isAssignableFrom(c)) {
        System.out.println(name + ": not a NotificationBroadcaster");
        continue;
      }
      if (Modifier.isAbstract(c.getModifiers())) {
        System.out.println(name + ": abstract class");
        continue;
      }

      NotificationBroadcaster mbean;
      Constructor<?> constr;
      try {
        constr = c.getConstructor();
      } catch (Exception e) {
        System.out.println(name + ": no public no-arg constructor: " + e);
        continue;
      }
      try {
        mbean = (NotificationBroadcaster) constr.newInstance();
      } catch (Exception e) {
        System.out.println(name + ": no-arg constructor failed: " + e);
        continue;
      }

      check(mbean);
    }

    System.out.println();
    System.out.println("Testing some explicit cases...");

    check(new RelationService(false));
    /*
      We can't do this:
        check(new RequiredModelMBean());
      because the Model MBean spec more or less forces us to use the
      names GENERIC and ATTRIBUTE_CHANGE for its standard notifs.
    */
    checkRMIConnectorServer();

    System.out.println();
    if (!suspicious.isEmpty()) System.out.println("SUSPICIOUS CLASSES: " + suspicious);

    if (failed.isEmpty()) System.out.println("TEST PASSED");
    else {
      System.out.println("TEST FAILED: " + failed);
      System.exit(1);
    }
  }
Exemple #20
0
 /** Parses command line arguments. */
 boolean parseArgs(String args[]) {
   /* Preprocess and expand @file arguments */
   try {
     args = CommandLine.parse(args);
   } catch (FileNotFoundException e) {
     fatalError(formatMsg("error.cant.open", e.getMessage()));
     return false;
   } catch (IOException e) {
     fatalError(e);
     return false;
   }
   /* parse flags */
   int count = 1;
   try {
     String flags = args[0];
     if (flags.startsWith("-")) {
       flags = flags.substring(1);
     }
     for (int i = 0; i < flags.length(); i++) {
       switch (flags.charAt(i)) {
         case 'c':
           if (xflag || tflag || uflag || iflag) {
             usageError();
             return false;
           }
           cflag = true;
           break;
         case 'u':
           if (cflag || xflag || tflag || iflag) {
             usageError();
             return false;
           }
           uflag = true;
           break;
         case 'x':
           if (cflag || uflag || tflag || iflag) {
             usageError();
             return false;
           }
           xflag = true;
           break;
         case 't':
           if (cflag || uflag || xflag || iflag) {
             usageError();
             return false;
           }
           tflag = true;
           break;
         case 'M':
           Mflag = true;
           break;
         case 'v':
           vflag = true;
           break;
         case 'f':
           fname = args[count++];
           break;
         case 'm':
           mname = args[count++];
           break;
         case '0':
           flag0 = true;
           break;
         case 'i':
           if (cflag || uflag || xflag || tflag) {
             usageError();
             return false;
           }
           // do not increase the counter, files will contain rootjar
           rootjar = args[count++];
           iflag = true;
           break;
         case 'e':
           ename = args[count++];
           break;
         case 'p':
           pname = args[count++];
           break;
         default:
           error(formatMsg("error.illegal.option", String.valueOf(flags.charAt(i))));
           usageError();
           return false;
       }
     }
   } catch (ArrayIndexOutOfBoundsException e) {
     usageError();
     return false;
   }
   if (!cflag && !tflag && !xflag && !uflag && !iflag) {
     error(getMsg("error.bad.option"));
     usageError();
     return false;
   }
   /* parse file arguments */
   int n = args.length - count;
   if (n > 0) {
     int k = 0;
     String[] nameBuf = new String[n];
     try {
       for (int i = count; i < args.length; i++) {
         if (args[i].equals("-C")) {
           /* change the directory */
           String dir = args[++i];
           dir = (dir.endsWith(File.separator) ? dir : (dir + File.separator));
           dir = dir.replace(File.separatorChar, '/');
           while (dir.indexOf("//") > -1) {
             dir = dir.replace("//", "/");
           }
           paths.add(dir.replace(File.separatorChar, '/'));
           nameBuf[k++] = dir + args[++i];
         } else {
           nameBuf[k++] = args[i];
         }
       }
     } catch (ArrayIndexOutOfBoundsException e) {
       usageError();
       return false;
     }
     files = new String[k];
     System.arraycopy(nameBuf, 0, files, 0, k);
   } else if (cflag && (mname == null)) {
     error(getMsg("error.bad.cflag"));
     usageError();
     return false;
   } else if (uflag) {
     if ((mname != null) || (ename != null) || (pname != null)) {
       /* just want to update the manifest */
       return true;
     } else {
       error(getMsg("error.bad.uflag"));
       usageError();
       return false;
     }
   }
   return true;
 }