19784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar/*
29784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
39784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar *
49784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
59784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * you may not use this file except in compliance with the License.
69784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * You may obtain a copy of the License at
79784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar *
89784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
99784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar *
109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * Unless required by applicable law or agreed to in writing, software
119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * See the License for the specific language governing permissions and
149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * limitations under the License.
159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar */
169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarpackage android.databinding.tool;
189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport org.apache.commons.io.IOUtils;
209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.processing.Scope;
229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.processing.ScopedException;
239784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.util.L;
249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.util.Preconditions;
259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport android.databinding.tool.writer.JavaFileWriter;
269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.io.File;
289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.io.FileInputStream;
299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.io.FileNotFoundException;
309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.io.FileOutputStream;
319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.io.IOException;
329784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.io.InputStream;
339784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.util.ArrayList;
349784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.util.HashMap;
359784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.util.List;
369784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.util.Map;
379784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarimport java.util.Properties;
389784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
399784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
409784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar/**
419784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar * This class is used by Android Gradle plugin.
429784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar */
439784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar@SuppressWarnings("unused")
449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyarpublic class DataBindingBuilder {
459784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    Versions mVersions;
469784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private final static String EXCLUDE_PATTERN = "android/databinding/layouts/*.*";
479784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public String getCompilerVersion() {
489784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return getVersions().compiler;
499784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
509784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public String getBaseLibraryVersion(String compilerVersion) {
529784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return getVersions().baseLibrary;
539784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
549784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
559784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public String getLibraryVersion(String compilerVersion) {
569784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return getVersions().extensions;
579784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public String getBaseAdaptersVersion(String compilerVersion) {
609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return getVersions().extensions;
619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public void setPrintMachineReadableOutput(boolean machineReadableOutput) {
649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        ScopedException.encodeOutput(machineReadableOutput);
659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public boolean getPrintMachineReadableOutput() {
689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return ScopedException.issEncodeOutput();
699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public void setDebugLogEnabled(boolean enableDebugLogs) {
729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        L.setDebugLog(enableDebugLogs);
739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public boolean isDebugLogEnabled() {
769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return L.isDebugEnabled();
779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
789784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
799784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private Versions getVersions() {
809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        if (mVersions != null) {
819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return mVersions;
829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        try {
849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            Properties props = new Properties();
859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            InputStream stream = getClass().getResourceAsStream("/data_binding_version_info.properties");
869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            try {
879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                props.load(stream);
889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                mVersions = new Versions(props);
899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } finally {
909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                IOUtils.closeQuietly(stream);
919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        } catch (IOException exception) {
939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            L.e(exception, "Cannot read data binding version");
949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return mVersions;
969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
979784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
989784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    /**
999784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     * Returns the list of classes that should be excluded from package task
1009784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     *
1019784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     * @param layoutXmlProcessor The LayoutXmlProcessor for this variant
1029784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     * @param generatedClassListFile The location of the File into which data binding compiler wrote
1039784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     *                               list of generated classes
1049784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     *
1059784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     * @return The list of classes to exclude. They are already in JNI format.
1069784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar     */
1079784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public List<String> getJarExcludeList(LayoutXmlProcessor layoutXmlProcessor,
1089784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            File generatedClassListFile) {
1099784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<String> excludes = new ArrayList<String>();
1109784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        String appPkgAsClass = layoutXmlProcessor.getPackage().replace('.', '/');
1119784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        String infoClassAsClass = layoutXmlProcessor.getInfoClassFullName().replace('.', '/');
1129784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1139784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        excludes.add(infoClassAsClass + ".class");
1149784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        excludes.add(EXCLUDE_PATTERN);
1159784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        excludes.add(appPkgAsClass + "/BR.*");
1169784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        excludes.add("android/databinding/DynamicUtil.class");
1179784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        if (generatedClassListFile != null) {
1189784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            List<String> generatedClasses = readGeneratedClasses(generatedClassListFile);
1199784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            for (String klass : generatedClasses) {
1209784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                excludes.add(klass.replace('.', '/') + ".class");
1219784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1229784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1239784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Scope.assertNoError();
1249784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return excludes;
1259784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
1269784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1279784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private List<String> readGeneratedClasses(File generatedClassListFile) {
1289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Preconditions.checkNotNull(generatedClassListFile, "Data binding exclude generated task"
1299784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                + " is not configured properly");
1309784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        Preconditions.check(generatedClassListFile.exists(),
1319784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                "Generated class list does not exist %s", generatedClassListFile.getAbsolutePath());
1329784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        FileInputStream fis = null;
1339784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        try {
1349784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            fis = new FileInputStream(generatedClassListFile);
1359784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            return IOUtils.readLines(fis);
1369784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        } catch (FileNotFoundException e) {
1379784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            L.e(e, "Unable to read generated class list from %s",
1389784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    generatedClassListFile.getAbsoluteFile());
1399784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        } catch (IOException e) {
1409784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            L.e(e, "Unexpected exception while reading %s",
1419784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    generatedClassListFile.getAbsoluteFile());
1429784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        } finally {
1439784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            IOUtils.closeQuietly(fis);
1449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1459784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        L.e("Could not read data binding generated class list");
1469784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return null;
1479784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
1489784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1499784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    public JavaFileWriter createJavaFileWriter(File outFolder) {
1509784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        return new GradleFileWriter(outFolder.getAbsolutePath());
1519784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
1529784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1539784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    static class GradleFileWriter extends JavaFileWriter {
1549784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1559784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        private final String outputBase;
1569784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1579784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public GradleFileWriter(String outputBase) {
1589784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            this.outputBase = outputBase;
1599784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1609784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1619784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        @Override
1629784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public void writeToFile(String canonicalName, String contents) {
1639784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            String asPath = canonicalName.replace('.', '/');
1649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            File f = new File(outputBase + "/" + asPath + ".java");
1659784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            //noinspection ResultOfMethodCallIgnored
1669784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            f.getParentFile().mkdirs();
1679784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            FileOutputStream fos = null;
1689784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            try {
1699784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                fos = new FileOutputStream(f);
1709784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                IOUtils.write(contents, fos);
1719784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } catch (IOException e) {
1729784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                L.e(e, "cannot write file " + f.getAbsolutePath());
1739784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            } finally {
1749784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                IOUtils.closeQuietly(fos);
1759784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            }
1769784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1779784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
1789784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1799784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    private static class Versions {
1809784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final String compilerCommon;
1819784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final String compiler;
1829784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final String baseLibrary;
1839784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        final String extensions;
1849784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar
1859784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        public Versions(Properties properties) {
1869784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            compilerCommon = properties.getProperty("compilerCommon");
1879784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            compiler = properties.getProperty("compiler");
1889784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            baseLibrary = properties.getProperty("baseLibrary");
1899784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            extensions = properties.getProperty("extensions");
1909784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            Preconditions.checkNotNull(compilerCommon, "cannot read compiler common version");
1919784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            Preconditions.checkNotNull(compiler, "cannot read compiler version");
1929784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            Preconditions.checkNotNull(baseLibrary, "cannot read baseLibrary version");
1939784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            Preconditions.checkNotNull(extensions, "cannot read extensions version");
1949784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        }
1959784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar    }
1969784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar}