tor$.visitFile(Path,BasicFileAttributes)
last analyzed

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 5
ccs 2
cts 2
cp 1
1
/*
2
 * This file is part of ArakneLangLoader.
3
 *
4
 * ArakneLangLoader is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * ArakneLangLoader is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with ArakneLangLoader.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2020 Vincent Quatrevieux
18
 */
19
20
package fr.arakne.swflangloader.loader;
21
22
import com.jpexs.decompiler.flash.AbortRetryIgnoreHandler;
23
import com.jpexs.decompiler.flash.SWF;
24
import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode;
25
import com.jpexs.decompiler.flash.exporters.settings.ScriptExportSettings;
26
import fr.arakne.swflangloader.parser.mapper.MapperHydrator;
27
28
import java.io.*;
29
import java.net.URL;
30
import java.nio.file.*;
31
import java.nio.file.attribute.BasicFileAttributes;
32
import java.util.Collections;
33
import java.util.List;
34
import java.util.stream.Collectors;
35
import java.util.stream.Stream;
36
37
/**
38
 * Load the SWF file to extract AS3 sources
39
 */
40
final public class SwfFileLoader {
41
    final private Path tempDir;
42
    final private boolean cache;
43
44
    public SwfFileLoader() {
45 1
        this(Paths.get("./tmp"), true);
46 1
    }
47
48
    /**
49
     * @param tempDir The temporary directory used for extract action script files
50
     * @param cache Enable cache (i.e. keep tmp files)
51
     */
52 1
    public SwfFileLoader(Path tempDir, boolean cache) {
53 1
        this.tempDir = tempDir;
54 1
        this.cache = cache;
55 1
    }
56
57
    /**
58
     * Load the SWF file and hydrate the target structure
59
     *
60
     * @param file SWF file URL
61
     * @param target The target structure
62
     * @param hydrator Hydrator to use
63
     * @param <T> The structure type
64
     *
65
     * @throws IOException When error occurs during loading the SWF file
66
     */
67
    public synchronized <T extends AbstractSwfFile> void load(URL file, T target, MapperHydrator<T> hydrator) throws IOException, InterruptedException {
68 1
        final String filename = extractFilename(file);
69 1
        parseFilename(target, filename);
70
71 1
        Path outdir = tempDir.resolve(filename);
72 1
        Files.createDirectories(outdir);
73
74
        try {
75 1
            List<File> sources = loadFilesFromCache(outdir);
76
77 1
            if (sources.isEmpty()) {
78 1
                sources = loadFilesFromSwf(file, outdir);
79
            }
80
81 1
            for (File source : sources) {
82 1
                parseFile(source, target, hydrator);
83 1
            }
84
        } finally {
85 1
            if (!cache) {
86 1
                clearTemp(tempDir);
87
            }
88
        }
89 1
    }
90
91
    /**
92
     * Clear the cache
93
     *
94
     * @throws IOException When removing cache files failed
95
     */
96
    public void clear() throws IOException {
97 1
        clearTemp(tempDir);
98 1
    }
99
100
    private String extractFilename(URL file) {
101 1
        final String path = file.getFile();
102 1
        final int filenamePos = path.lastIndexOf('/');
103
104 1
        return path.substring(filenamePos + 1, path.length() - 4);
105
    }
106
107
    private void parseFilename(AbstractSwfFile target, String filename) {
108 1
        final String[] parts = filename.split("_", 3);
109
110 1
        if (parts.length != 3) {
111
            return;
112
        }
113
114 1
        target.setName(parts[0]);
115 1
        target.setLanguage(parts[1]);
116
117
        try {
118 1
            target.setVersion(Integer.parseInt(parts[2]));
119
        } catch (NumberFormatException e) {
120
            // Ignore
121 1
        }
122 1
    }
123
124
    private List<File> loadFilesFromSwf(URL url, Path outdir) throws IOException, InterruptedException {
125 1
        try (InputStream stream = url.openStream()) {
126 1
            SWF swf = new SWF(stream, false);
127
128 1
            return swf.exportActionScript(
129 1
                new AbortRetryIgnoreHandler() {
130
                    @Override
131
                    public int handle(Throwable throwable) {
132
                        return AbortRetryIgnoreHandler.ABORT;
133
                    }
134
135
                    @Override
136
                    public AbortRetryIgnoreHandler getNewInstance() {
137
                        return this;
138
                    }
139
                },
140 1
                outdir.toAbsolutePath().toString(),
141
                new ScriptExportSettings(ScriptExportMode.AS, false, true),
142
                false,
143
                null
144
            );
145
        }
146
    }
147
148
    private List<File> loadFilesFromCache(Path cacheDir) throws IOException {
149 1
        if (!cache) {
150 1
            return Collections.emptyList();
151
        }
152
153 1
        try (final Stream<Path> files = Files.walk(cacheDir)) {
154 1
            return files
155 1
                .filter(Files::isRegularFile)
156 1
                .filter(path -> path.endsWith(".as"))
157 1
                .map(Path::toFile)
158 1
                .collect(Collectors.toList())
159
            ;
160
        }
161
    }
162
163
    /**
164
     * Parse file lines
165
     */
166
    private <T> void parseFile(File file, T target, MapperHydrator<T> hydrator) throws IOException {
167 1
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(file.toPath())))) {
168 1
            for (String line = reader.readLine(); line != null; line = reader.readLine()) {
169 1
                hydrator.hydrate(target, line);
170
            }
171
        }
172 1
    }
173
174
    /**
175
     * Recursive clear cache directory
176
     */
177
    private void clearTemp(Path tempdir) throws IOException {
178 1
        if (!Files.exists(tempdir)) {
179 1
            return;
180
        }
181
182 1
        Files.walkFileTree(tempdir, new SimpleFileVisitor<Path>() {
183
            @Override
184
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
185 1
                Files.delete(dir);
186
187 1
                return FileVisitResult.CONTINUE;
188
            }
189
190
            @Override
191
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
192 1
                Files.delete(file);
193
194 1
                return FileVisitResult.CONTINUE;
195
            }
196
        });
197 1
    }
198
}
199