Example usage for org.apache.commons.compress.archivers.tar TarArchiveOutputStream TarArchiveOutputStream

List of usage examples for org.apache.commons.compress.archivers.tar TarArchiveOutputStream TarArchiveOutputStream

Introduction

In this page you can find the example usage for org.apache.commons.compress.archivers.tar TarArchiveOutputStream TarArchiveOutputStream.

Prototype

public TarArchiveOutputStream(OutputStream os) 

Source Link

Document

Constructor for TarInputStream.

Usage

From source file:com.mobilesorcery.sdk.builder.linux.deb.DebBuilder.java

/**
 * Adds the files in the file list in a tar+gz
 *
 * @param o Output file/* w  w  w  .ja v a  2  s. com*/
 *
 * @throws IOException If error occurs during writing
 * @throws FileNotFoundException If the output file could not be opened.
 */
private void doAddFilesToTarGZip(File o) throws IOException, FileNotFoundException

{
    FileOutputStream os = new FileOutputStream(o);
    GzipCompressorOutputStream gzos = new GzipCompressorOutputStream(os);
    TarArchiveOutputStream tos = new TarArchiveOutputStream(gzos);

    // Add files
    for (SimpleEntry<File, SimpleEntry<String, Integer>> fileEntry : m_fileList) {
        File file = fileEntry.getKey();
        String name = fileEntry.getValue().getKey();
        int mode = fileEntry.getValue().getValue();
        TarArchiveEntry e = new TarArchiveEntry(file, name);

        // Add to tar, user/group id 0 is always root
        e.setMode(mode);
        e.setUserId(0);
        e.setUserName("root");
        e.setGroupId(0);
        e.setGroupName("root");
        tos.putArchiveEntry(e);

        // Write bytes
        if (file.isFile())
            BuilderUtil.getInstance().copyFileToOutputStream(tos, file);
        tos.closeArchiveEntry();
    }

    // Done
    tos.close();
    gzos.close();
    os.close();
}

From source file:com.redhat.red.offliner.ftest.SinglePlaintextDownloadOfTarballFTest.java

private File makeTarball(final Map<String, byte[]> entries) throws IOException {
    File tgz = temporaryFolder.newFile();
    try (TarArchiveOutputStream tarOut = new TarArchiveOutputStream(
            new GzipCompressorOutputStream(new FileOutputStream(tgz)))) {
        entries.forEach((name, content) -> {
            try {
                File entryFile = temporaryFolder.newFile();
                FileUtils.writeByteArrayToFile(entryFile, content);

                TarArchiveEntry entry = new TarArchiveEntry(entryFile, name);
                //                entry.setSize( content.length );
                //                entry.setMode( 0644 );
                //                entry.setGroupId( 1000 );
                //                entry.setUserId( 1000 );

                tarOut.putArchiveEntry(entry);

                System.out.printf("Entry: %s mode: '0%s'\n", entry.getName(),
                        Integer.toString(entry.getMode(), 8));

                tarOut.write(content);/* ww  w .j  ava2  s . co  m*/
                tarOut.closeArchiveEntry();
            } catch (IOException e) {
                e.printStackTrace();
                fail("Failed to write tarball");
            }
        });

        tarOut.flush();
    }

    try (TarArchiveInputStream tarIn = new TarArchiveInputStream(
            new GzipCompressorInputStream(new FileInputStream(tgz)))) {
        TarArchiveEntry entry = null;
        while ((entry = tarIn.getNextTarEntry()) != null) {
            byte[] entryData = new byte[(int) entry.getSize()];
            int read = tarIn.read(entryData, 0, entryData.length);
            assertThat("Not enough bytes read for: " + entry.getName(), read, equalTo((int) entry.getSize()));
            assertThat(entry.getName() + ": data doesn't match input",
                    Arrays.equals(entries.get(entry.getName()), entryData), equalTo(true));
        }
    }

    return tgz;
}

From source file:com.st.maven.debian.DebianPackageMojo.java

private void fillControlTar(Config config, ArFileOutputStream output) throws MojoExecutionException {
    TarArchiveOutputStream tar = null;/*from  www.java 2s .  com*/
    try {
        tar = new TarArchiveOutputStream(new GZIPOutputStream(new ArWrapper(output)));
        tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
        TarArchiveEntry rootDir = new TarArchiveEntry("./");
        tar.putArchiveEntry(rootDir);
        tar.closeArchiveEntry();

        byte[] controlData = processTemplate(freemarkerConfig, config, "control.ftl");
        TarArchiveEntry controlEntry = new TarArchiveEntry("./control");
        controlEntry.setSize(controlData.length);
        tar.putArchiveEntry(controlEntry);
        tar.write(controlData);
        tar.closeArchiveEntry();

        byte[] preinstBaseData = processTemplate("preinst", freemarkerConfig, config,
                combine("preinst.ftl", BASE_DIR + File.separator + "preinst", false));
        long size = preinstBaseData.length;
        TarArchiveEntry preinstEntry = new TarArchiveEntry("./preinst");
        preinstEntry.setSize(size);
        preinstEntry.setMode(0755);
        tar.putArchiveEntry(preinstEntry);
        tar.write(preinstBaseData);
        tar.closeArchiveEntry();

        byte[] postinstBaseData = processTemplate("postinst", freemarkerConfig, config,
                combine("postinst.ftl", BASE_DIR + File.separator + "postinst", true));
        size = postinstBaseData.length;
        TarArchiveEntry postinstEntry = new TarArchiveEntry("./postinst");
        postinstEntry.setSize(size);
        postinstEntry.setMode(0755);
        tar.putArchiveEntry(postinstEntry);
        tar.write(postinstBaseData);
        tar.closeArchiveEntry();

        byte[] prermBaseData = processTemplate("prerm", freemarkerConfig, config,
                combine("prerm.ftl", BASE_DIR + File.separator + "prerm", false));
        size = prermBaseData.length;
        TarArchiveEntry prermEntry = new TarArchiveEntry("./prerm");
        prermEntry.setSize(size);
        prermEntry.setMode(0755);
        tar.putArchiveEntry(prermEntry);
        tar.write(prermBaseData);
        tar.closeArchiveEntry();

        byte[] postrmBaseData = processTemplate("postrm", freemarkerConfig, config,
                combine("postrm.ftl", BASE_DIR + File.separator + "postrm", false));
        size = postrmBaseData.length;
        TarArchiveEntry postrmEntry = new TarArchiveEntry("./postrm");
        postrmEntry.setSize(size);
        postrmEntry.setMode(0755);
        tar.putArchiveEntry(postrmEntry);
        tar.write(postrmBaseData);
        tar.closeArchiveEntry();

    } catch (Exception e) {
        throw new MojoExecutionException("unable to create control tar", e);
    } finally {
        if (tar != null) {
            try {
                tar.close();
            } catch (IOException e) {
                getLog().error("unable to finish tar", e);
            }
        }
    }
}

From source file:fr.ortolang.diffusion.api.content.ContentResource.java

private ResponseBuilder handleExport(boolean followSymlink, String filename, String format,
        final List<String> paths, final Pattern pattern) throws UnsupportedEncodingException {
    ResponseBuilder builder;/*from www .j av  a 2s  . c o m*/
    switch (format) {
    case "zip": {
        LOGGER.log(Level.FINE, "exporting using format zip");
        builder = Response.ok();
        builder.header("Content-Disposition",
                "attachment; filename*=UTF-8''" + URLEncoder.encode(filename, "utf-8") + ".zip");
        builder.type("application/zip");
        StreamingOutput stream = output -> {
            try (ZipArchiveOutputStream out = new ZipArchiveOutputStream(output)) {
                for (String path : paths) {
                    try {
                        String key = resolveContentPath(path);
                        ArchiveEntryFactory factory = (name, time, size) -> {
                            ZipArchiveEntry entry = new ZipArchiveEntry(name);
                            if (time != -1) {
                                entry.setTime(time);
                            }
                            if (size != -1) {
                                entry.setSize(size);
                            }
                            return entry;
                        };
                        exportToArchive(key, out, factory, PathBuilder.fromPath(path), false, pattern);
                    } catch (AccessDeniedException e) {
                        LOGGER.log(Level.FINEST, "access denied during export to zip", e);
                    } catch (BrowserServiceException | CoreServiceException | AliasNotFoundException
                            | KeyNotFoundException | OrtolangException e) {
                        LOGGER.log(Level.INFO, "unable to export path to zip", e);
                    } catch (InvalidPathException e) {
                        LOGGER.log(Level.FINEST, "invalid path during export to zip", e);
                    } catch (PathNotFoundException e) {
                        LOGGER.log(Level.FINEST, "path not found during export to zip", e);
                    } catch (ExportToArchiveIOException e) {
                        LOGGER.log(Level.SEVERE,
                                "unexpected IO error during export to archive, stopping export", e);
                        break;
                    }
                }
            }
        };
        builder.entity(stream);
        break;
    }
    case "tar": {
        LOGGER.log(Level.FINE, "exporting using format tar");
        builder = Response.ok();
        builder.header("Content-Disposition",
                "attachment; filename*=UTF-8''" + URLEncoder.encode(filename, "utf-8") + ".tar.gz");
        builder.type("application/x-gzip");
        StreamingOutput stream = output -> {
            try (GzipCompressorOutputStream gout = new GzipCompressorOutputStream(output);
                    TarArchiveOutputStream out = new TarArchiveOutputStream(gout)) {
                for (String path : paths) {
                    try {
                        String key = resolveContentPath(path);
                        ArchiveEntryFactory factory = (name, time, size) -> {
                            TarArchiveEntry entry = new TarArchiveEntry(name);
                            if (time != -1) {
                                entry.setModTime(time);
                            }
                            if (size != -1) {
                                entry.setSize(size);
                            }
                            return entry;
                        };
                        exportToArchive(key, out, factory, PathBuilder.fromPath(path), false, pattern);
                    } catch (BrowserServiceException | CoreServiceException | AliasNotFoundException
                            | KeyNotFoundException | OrtolangException e) {
                        LOGGER.log(Level.INFO, "unable to export path to tar", e);
                    } catch (InvalidPathException e) {
                        LOGGER.log(Level.FINEST, "invalid path during export to tar", e);
                    } catch (PathNotFoundException e) {
                        LOGGER.log(Level.FINEST, "path not found during export to tar", e);
                    } catch (ExportToArchiveIOException e) {
                        LOGGER.log(Level.SEVERE,
                                "unexpected IO error during export to archive, stopping export", e);
                        break;
                    }
                }
            }
        };
        builder.entity(stream);
        break;
    }
    default:
        builder = Response.status(Status.BAD_REQUEST).entity("export format [" + format + "] is not supported");
    }
    return builder;
}

From source file:io.anserini.index.IndexUtils.java

public void dumpRawDocuments(String reqDocidsPath, boolean prependDocid)
        throws IOException, NotStoredException {
    LOG.info("Start dump raw documents" + (prependDocid ? " with Docid prepended" : "."));

    InputStream in = getReadFileStream(reqDocidsPath);
    BufferedReader bRdr = new BufferedReader(new InputStreamReader(in));
    FileOutputStream fOut = new FileOutputStream(new File(reqDocidsPath + ".output.tar.gz"));
    BufferedOutputStream bOut = new BufferedOutputStream(fOut);
    GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(bOut);
    TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut);

    String docid;//from w ww.  ja  v a 2 s .com
    int counter = 0;
    while ((docid = bRdr.readLine()) != null) {
        counter += 1;
        Document d = reader.document(convertDocidToLuceneDocid(docid));
        IndexableField doc = d.getField(LuceneDocumentGenerator.FIELD_RAW);
        if (doc == null) {
            throw new NotStoredException("Raw documents not stored!");
        }
        TarArchiveEntry tarEntry = new TarArchiveEntry(new File(docid));

        byte[] bytesOut = doc.stringValue().getBytes(StandardCharsets.UTF_8);
        tarEntry.setSize(
                bytesOut.length + (prependDocid ? String.format("<DOCNO>%s</DOCNO>\n", docid).length() : 0));
        tOut.putArchiveEntry(tarEntry);
        if (prependDocid) {
            tOut.write(String.format("<DOCNO>%s</DOCNO>\n", docid).getBytes());
        }
        tOut.write(bytesOut);
        tOut.closeArchiveEntry();

        if (counter % 100000 == 0) {
            LOG.info(counter + " files have been dumped.");
        }
    }
    tOut.close();
    LOG.info(String.format("Raw documents are output to: %s", reqDocidsPath + ".output.tar.gz"));
}

From source file:com.baidu.rigel.biplatform.tesseract.util.FileUtils.java

/**
 * Perform file compression.//from w ww .  ja  v  a2  s .c  om
 * 
 * @param inFileName
 *            Name of the file to be compressed
 * @throws IOException
 */
public static String doCompressFile(String inFileName, String outFileName) throws IOException {
    LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_FUNCTION_BEGIN, "doCompressFile",
            "[inFileName:" + inFileName + "]"));
    FileOutputStream fOut = null;
    BufferedOutputStream bOut = null;
    GzipCompressorOutputStream gzOut = null;
    TarArchiveOutputStream tOut = null;
    if (StringUtils.isEmpty(inFileName)) {
        throw new IllegalArgumentException();
    }
    String compressedFileName = outFileName;

    FileInputStream fi = null;
    BufferedInputStream sourceStream = null;

    try {

        LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_COMPRESS_PROCESS,
                "Creating the GZIP output stream"));

        /** Step: 1 ---> create a TarArchiveOutputStream object. **/
        fOut = new FileOutputStream(new File(compressedFileName));
        bOut = new BufferedOutputStream(fOut);
        gzOut = new GzipCompressorOutputStream(bOut);
        tOut = new TarArchiveOutputStream(gzOut);

        /**
         * Step: 2 --->Open the source data and get a list of files from
         * given directory.
         */
        File source = new File(inFileName);
        if (!source.exists()) {
            LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_COMPRESS_ERROR,
                    "File not found. " + inFileName));
            return null;
        }
        File[] files = null;
        if (source.isDirectory()) {
            files = source.listFiles();
        } else {
            files = new File[1];
            files[0] = source;
        }

        for (int i = 0; i < files.length; i++) {
            LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_COMPRESS_PROCESS,
                    "Adding File:" + source.getParentFile().toURI().relativize(files[i].toURI()).getPath()));
            /**
             * Step: 3 ---> Create a tar entry for each file that is read.
             */
            /**
             * relativize is used to to add a file to a tar, without
             * including the entire path from root.
             */

            TarArchiveEntry entry = new TarArchiveEntry(files[i],
                    source.getParentFile().toURI().relativize(files[i].toURI()).getPath());
            /**
             * Step: 4 ---> Put the tar entry using putArchiveEntry.
             */
            tOut.putArchiveEntry(entry);

            /**
             * Step: 5 ---> Write the data to the tar file and close the
             * input stream.
             */

            fi = new FileInputStream(files[i]);
            sourceStream = new BufferedInputStream(fi, TesseractConstant.FILE_BLOCK_SIZE);
            int count;
            byte[] data = new byte[TesseractConstant.FILE_BLOCK_SIZE];
            while ((count = sourceStream.read(data, 0, TesseractConstant.FILE_BLOCK_SIZE)) != -1) {
                tOut.write(data, 0, count);
            }

            sourceStream.close();

            /**
             * Step: 6 --->close the archive entry.
             */

            tOut.closeArchiveEntry();

        }

        /**
         * Step: 7 --->close the output stream.
         */

        tOut.close();

    } catch (IOException e) {
        LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_COMPRESS_ERROR, "IOException"));
        LOGGER.error(e.getMessage(), e);
        throw e;

    } finally {
        try {
            fOut.close();
            bOut.close();
            gzOut.close();
            tOut.close();
            fi.close();
            sourceStream.close();
        } catch (IOException e) {
            LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_COMPRESS_ERROR,
                    "IOException occur when closing fd"));
            LOGGER.error(e.getMessage(), e);
            throw e;
        }

    }

    LOGGER.info(String.format(LogInfoConstants.INFO_PATTERN_FUNCTION_END, "doCompressFile",
            "[inFileName:" + inFileName + "][compressedFileName:" + compressedFileName + "]"));

    return compressedFileName;
}

From source file:ezbake.protect.ezca.EzCABootstrap.java

public static void createAndWriteTarball(String name, AppCerts certs, String filePath) {
    TarArchiveOutputStream os = null;//  w  w w.  j ava 2s. co m
    try {
        File outputFile = new File(filePath, name + ".tar.gz");
        outputFile.createNewFile();
        outputFile.setWritable(false, false);
        outputFile.setWritable(true, true);
        outputFile.setReadable(false, false);
        outputFile.setReadable(true, true);
        FileOutputStream fos = new FileOutputStream(outputFile);

        CompressorOutputStream cos = new CompressorStreamFactory()
                .createCompressorOutputStream(CompressorStreamFactory.GZIP, fos);
        os = new TarArchiveOutputStream(cos);

        // For each field in the app certs, create an entry in the tar archive
        for (AppCerts._Fields field : AppCerts._Fields.values()) {
            Object o = certs.getFieldValue(field);
            if (o instanceof byte[]) {
                String fieldName = field.getFieldName().replace("_", ".");
                addTarArchiveEntry(os, fieldName, (byte[]) o);
            }
        }

    } catch (FileNotFoundException e) {
        logger.error("Unable to write tarball", e);
    } catch (CompressorException e) {
        logger.error("Error compressing tarball", e);
    } catch (IOException e) {
        logger.error("Error creating output file for tarball", e);
    } finally {
        if (os != null) {
            try {
                os.finish();
                os.close();
            } catch (IOException e) {
                logger.warn("Unable to close output stream", e);
            }
        }
    }
}

From source file:bb.io.TarUtil.java

/**
* Writes each element of pathsToArchive to a new TAR format archive file specified by tarFile.
* If any element is a directory, the entire contents of its directory tree will be archived (as limited by filter).
* Paths that would otherwise be archived may be screened out by supplying a non null value for filter.
* <p>/* w  w  w.  j  a v  a2 s  .co  m*/
* Altho this method does not use {@link DirUtil#getTree DirUtil.getTree},
* it uses filter to control subdirectory exploration in a similar manner.
* <p>
* In general, the path stored in the archive
* is the path relative to the <i>parent</i> of the relevant element of pathsToArchive.
* For example, suppose that some element of pathsToArchive corresponds to <code>D:/someDirectory</code>,
* and suppose that that directory contains the subdirectory and child file <code>D:/someDirectory/anotherDirectory/childFile</code>.
* Then the paths stored in the archive are <code>anotherDirectory</code> and <code>anotherDirectory/childFile</code> respectively.
* <p>
* One complication with the above scheme is paths which are file system roots: they have no parents.
* Examples include the windows path <code>C:</code> or the unix path <code>/</code>.
* In cases like these, this method uses an imaginary parent name of the form <code>rootXXX</code> (where XXX is an integer).
* For example, on a windows machine, if pathsToArchive contains the paths <code>C:</code> and <code>D:</code>,
* then the contents of <code>C:</code> might be stored in the archive
* with a path that starts with <code>root1</code>, and the contents of <code>D:</code>
* may have an archive path that starts with <code>root2</code>.
* This behavior ensures that the archive preserves the separate origins of the 2 sources,
* which is necessary so that they do not get mixed when extracted.
* <p>
* The TAR archive witten by this method will use GNU TAR rules for the entry headers if long path names are encountered.
* <i>This means that standard POSIX compliant programs that do not support the GNU TAR extension
* will be unable to extract the contents.</i>
* <p>
* Optional GZIP compression may also be done.
* Normally, tarFile must be a path which ends in a ".tar" (case insensitive) extension.
* However, this method will also accept either ".tar.gz" or ".tgz" extensions,
* in which case it will perform GZIP compression on tarFile as part of archiving.
* <p>
* @param tarFile the TAR File that will write the archive data to
* @param filter a FileFilter that can use to screen out paths from being written to the archive;
* may be null, which means everything inside pathsToArchive gets archived;
* if not null, see warnings in {@link DirUtil#getTree DirUtil.getTree} on directory acceptance
* @param pathsToArchive array of all the paths to archive
* @throws Exception if any Throwable is caught; the Throwable is stored as the cause, and the message stores the path of tarFile;
* here are some of the possible causes:
* <ol>
*  <li>
*      IllegalArgumentException if pathsToArchive == null; pathsToArchive.length == 0;
*      tarFile == null;
*      tarFile already exists and either is not a normal file or is but already has data inside it;
*      tarFile has an invalid extension;
*      any element of pathsToArchive is null, does not exist, cannot be read, is equal to tarFile, its path contains tarFile,
*      or it fails {@link #isTarable isTarable}
*  </li>
*  <li>SecurityException if a security manager exists and its SecurityManager.checkRead method denies read access to some path</li>
*  <li>IOException if an I/O problem occurs</li>
* </ol>
*/
public static void archive(File tarFile, FileFilter filter, File... pathsToArchive) throws Exception {
    try {
        Check.arg().notNull(tarFile);
        if (tarFile.exists()) {
            if (!tarFile.isFile())
                throw new IllegalArgumentException(
                        "tarFile = " + tarFile.getPath() + " exists but is not a normal file");
            if (tarFile.length() != 0)
                throw new IllegalArgumentException("tarFile = " + tarFile.getPath()
                        + " already exists and already has data inside it; this method will not overwrite it");
        }
        Check.arg().notEmpty(pathsToArchive);
        for (int i = 0; i < pathsToArchive.length; i++) {
            Check.arg().notNull(pathsToArchive[i]);
            if (!pathsToArchive[i].exists())
                throw new IllegalArgumentException("pathsToArchive[" + i + "] = " + pathsToArchive[i].getPath()
                        + " is a non-existent path");
            if (!pathsToArchive[i].canRead())
                throw new IllegalArgumentException("pathsToArchive[" + i + "] = " + pathsToArchive[i].getPath()
                        + " cannot be read by this application");
            if (pathsToArchive[i].equals(tarFile))
                throw new IllegalArgumentException("pathsToArchive[" + i + "] = " + pathsToArchive[i].getPath()
                        + " is the same path as tarFile = " + tarFile.getPath());
            if (pathsToArchive[i].isDirectory() && DirUtil.contains(pathsToArchive[i], tarFile))
                throw new IllegalArgumentException("the directory corresponding to pathsToArchive[" + i + "] = "
                        + pathsToArchive[i].getCanonicalPath() + " will contain the path of tarFile = "
                        + tarFile.getCanonicalPath());
        }

        TarArchiveOutputStream taos = null;
        try {
            taos = new TarArchiveOutputStream(getOutputStream(tarFile));
            taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
            for (File path : pathsToArchive) {
                archive(path, new FileParent(path), taos, filter);
            }
        } finally {
            if (giveUserFeedback)
                ConsoleUtil.eraseLine();
            StreamUtil.close(taos);
        }
    } catch (Throwable t) {
        throw new Exception("See cause for the underlying Throwable; happened for tarFile = "
                + (tarFile != null ? tarFile.getPath() : "<null>"), t);
    }
}

From source file:lucee.commons.io.compress.CompressUtil.java

public static void compressTar(Resource[] sources, OutputStream target, int mode) throws IOException {
    if (target instanceof TarArchiveOutputStream) {
        compressTar("", sources, (TarArchiveOutputStream) target, mode);
        return;//  ww w .j  a v  a2 s .  c o m
    }
    TarArchiveOutputStream tos = new TarArchiveOutputStream(target);
    tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
    try {
        compressTar("", sources, tos, mode);
    } finally {
        IOUtil.closeEL(tos);
    }
}

From source file:edu.mit.lib.bagit.Filler.java

private void deflate(OutputStream out, String format) throws IOException {
    switch (format) {
    case "zip":
        ZipOutputStream zout = new ZipOutputStream(new BufferedOutputStream(out));
        fillZip(base, base.getName(), zout);
        zout.close();/*from   w ww . j  av  a 2 s.co m*/
        break;
    case "tgz":
        TarArchiveOutputStream tout = new TarArchiveOutputStream(
                new BufferedOutputStream(new GzipCompressorOutputStream(out)));
        fillArchive(base, base.getName(), tout);
        tout.close();
        break;
    default:
        throw new IOException("Unsupported package format: " + format);
    }
}